// iContact.cpp : Defines the entry point for the application.
//

#include "stdafx.h"
#include <snapi.h>
#include <windowsm.h>
#include <tpcshell.h>
#include <Sipapi.h>

#include "iContact.h"
#include "CommonWM.h"
#include "IniReader.h"
#include "Settings.h"
#include "Language.h"
#include "Theme.h"
#include "ListDataCallLog.h"
#include "ListDataPoom.h"
#include "GraphicFunctions.h"
#include "PhoneUtils.h"
#include "Titlebar.h"
#include "PoomPictures.h"
#include "PoomData.h"
#include "PoomPictureLoader.h"
#include "SimManager.h"
#include "EdbData.h"
#include "SearchKbd.h"
#include "ListDetails.h"
#include "ListCategories.h"

#ifdef _DEBUG
#include "crtdbg.h"
#endif

//-----------------------------------------------------------------------------
// Global data
//
#define MAX_VERSION_LENGTH 128
TCHAR g_szVersion[MAX_VERSION_LENGTH] = {0};
int   g_nVersionLen = 0;
const TCHAR szAppName[] = SZ_APP_NAME;
HINSTANCE   hInst = NULL;

bool  bInitializing = false;
int   nBusyProgress = 0;

CListBase*       pListData = NULL;
ListDataPoom*    pListDataFavorites = NULL;
ListDataCallLog* pListDataCallLog = NULL;
ListDataPoom*    pListDataContacts = NULL;
CListDetails*    pItemDetails = NULL;
CListCategories* pListCategories = NULL;

EScreenMode   CurrentScreenMode = smList;
EMainTabs     nCurrentTab = tabContacts;

EMouseArea    MouseArea = maNone;

bool          PopupShown = false;
int           PopupStartX = 0;
int           PopupStartY = 0;
EPopupWindows PopupWindow = pwNone;
RECT          rPopup;

SearchKbd*    pSearchKbd = NULL;
bool          SearchKbdShown = false;
ImeEditBox*   pSearchBox = NULL;

int           contactDetailsInHistoryIndex = -1;

// Graphic
RECT    rScreen;
int     nScreenHeight;
RECT    rTitlebar;
RECT    rMenubar;

HDC      hdcMem = NULL;
HBITMAP  hbmMem = NULL;
HDC      hdcTmp = NULL;
HBITMAP  hbmTmp = NULL;
HDC      hdcPage1 = NULL;
HBITMAP  hbmPage1 = NULL;
HDC      hdcPage2 = NULL;
HBITMAP  hbmPage2 = NULL;

// Scrolling
int     StartY;
int     LastY;
int     tStartTime;
int     tEndTime;
int     nKeyRepeatCount = 0;
int     lastKey = 0;

// Screen Transition
DWORD   dwTransitionStart = 0;
double  dTransitionPct = 0.0;
int     nTransitionDuration = 0;
bool    bTransitioning = false;
ETransitionType trTransitionType = ttSlideLeft;

// ignore mouseup
bool    bIsMouseDown = false;
bool    bIgnoreNextMouseUp = false;

// Keyboard Rows/Columns
int     nKeyboardRows = 0;
int     nKeyboardCols = 0;
int     GroupWidth = 0;     // Keyboard group width
int     GroupHeight = 0;    // Keyboard group height

//burt: mouse gestures begin
bool        MouseGesture_On = false;
DWORD       MouseGesture_StartTime = 0;
int         MouseMove_dx = 0;
int         MouseMove_dy = 0;
int         MouseMove_LastX = 0;
int         MouseMove_LastY = 0;
//burt: mouse gestures end
bool        MouseActionEnabled = true;
bool        SkipMouseAction = false;
bool        SkipKeyboardAction = false;
//avian: track mouse position globally
int     curMousePosX = 0;
int     curMousePosY = 0;

//avian: reload buffer
#define RELOAD_TIMER_INTERVAL 200
bool    reloadBuffered = false;
HWND    lasthwnd = NULL;


CallType    CallLog_ShownType = ctAll;

Menu*            pBottomMenu = NULL;
ListMenu*        pListMenu = NULL;
DetailsMenu*     pDetailsMenu = NULL;
SimDetailsMenu*  pSimDetailsMenu = NULL;
CallDetailsMenu* pCallDetailsMenu = NULL;

// Message dispatch table for MainWindowProc
const struct decodeUINT MainMessages[] = {
  WM_PAINT,     DoPaintMain,
  WM_DESTROY,   DoDestroyMain,
  WM_ACTIVATE,  DoActivate,
  WM_SIZE,      DoSize,
  WM_LBUTTONDOWN, DoLButtonDown,
  WM_MOUSEMOVE, DoMouseMove,
  WM_LBUTTONUP, DoLButtonUp,
  WM_TIMER,     DoTimer,
  WM_KEYDOWN,   DoKeyDown,
  WM_KEYUP,     DoKeyUp,
  WM_CHAR,      DoChar,
  WM_COMMAND,   DoCommand,
  WM_CTLCOLOREDIT,    DoCtlColorEdit,
  NM_TITLEBAR,        DoTitlebarCallback,
  WM_DBNOTIFICATION,      DoDbNotificationCallback,
  PIM_ITEM_CREATED_LOCAL, DoPoomContactCreated,
  PIM_ITEM_CREATED_REMOTE,DoPoomContactCreated,
  PIM_ITEM_DELETED_LOCAL, DoPoomContactDeleted,
  PIM_ITEM_DELETED_REMOTE,DoPoomContactDeleted,
  PIM_ITEM_CHANGED_LOCAL, DoPoomContactChanged,
  PIM_ITEM_CHANGED_REMOTE,DoPoomContactChanged,
  NM_SIM_MANAGER_CONTACTS_LOADED, DoSimContactRefresh,
  NM_SIM_MANAGER_CONTACT_CREATED, DoSimContactRefresh,
  NM_SIM_MANAGER_CONTACT_DELETED, DoSimContactRefresh,
  NM_POOM_PICTURE_LOADED, DoPoomPictureLoaded,
  NM_SKK_STRING_UPDATED,  DoSkkStringUpdated,
  NM_SKK_MODE_CHANGED,    DoSkkModeChanged,
  NM_SKK_CLOSE,           DoSkkClose,
  NM_THREAD_BUSY,         DoThreadBusy,
  NM_UPDATE_BUSY_PROGRESS,DoBusyProgress,
  WM_GET_CURRENT_TAB,     DoInfoGetCurrentTab,
  WM_GET_CALL_TYPE,       DoInfoGetCallType,
  WM_GET_CONTACTS_LIST,   DoInfoGetContactsList
};

#ifdef _DEBUG
LRESULT SendMessageDbg(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  return ::SendMessage(hWnd, uMsg, wParam, lParam);
}

LRESULT PostMessageDbg(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  return ::PostMessage(hWnd, uMsg, wParam, lParam);
}
#endif

//=============================================================================
//
// Program entry point
//
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    LPWSTR lpCmdLine, int nCmdShow)
{
#ifdef LOG_ENABLED
  BEGINLOG(TEXT("iContact.log"));
#endif

  try
  {
    MSG msg;
    HWND hwndMain;

#ifdef LOG_ENABLED
    LOGTIMER_START
#endif

      // Initialize this instance.
      hwndMain = InitInstance(hInstance, lpCmdLine, nCmdShow);
    if (hwndMain == 0)
      return 0x10;

#ifdef LOG_ENABLED
    LOGTIMER_LOG("Startup Time")
#endif

      // Application message loop
      while (GetMessage (&msg, NULL, 0, 0))
      {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
      }
      // Instance cleanup
      return TermInstance (hInstance, msg.wParam);
  }
  catch(const TCHAR* text)
  {
    ::MessageBox(NULL, text, TEXT("Fatal Error"), MB_OK | MB_ICONSTOP);
    return TermInstance(hInstance, 1);
  }
  catch(...)
  {
    ::MessageBox(NULL, TEXT("Unknown error"), TEXT("Fatal Error"), MB_OK | MB_ICONSTOP);
    return TermInstance(hInstance, 1);
  }

#ifdef LOG_ENABLED
  ENDLOG();
#endif
}

bool FindVersion()
{
  bool result = false;

  TCHAR szPath[MAX_PATH*2];
  ::GetModuleFileName(NULL, szPath, MAX_PATH*2);

  DWORD dwHandle;
  DWORD dwSize = ::GetFileVersionInfoSize(szPath, &dwHandle);
  if (dwSize > 0)
  {
    LPTSTR pData = (LPTSTR)malloc(dwSize);
    if (pData)
    {
      if (::GetFileVersionInfo(szPath, dwHandle, dwSize, pData))
      {
        UINT uInfoSize = 0;
        VS_FIXEDFILEINFO* pInfo = NULL;
        if (VerQueryValue(pData, TEXT("\\"), (LPVOID*)&pInfo, &uInfoSize))
        {
          int h1 = HIWORD(pInfo->dwFileVersionMS);
          int h2 = LOWORD(pInfo->dwFileVersionMS);
          int m1 = HIWORD(pInfo->dwFileVersionLS);
          int m2 = LOWORD(pInfo->dwFileVersionLS);
          TCHAR ver[64];
          if (h1)
            StringCchPrintf(ver, 64, TEXT("%d.%d.%d.%d"), h1, h2, m1, m2);
          else
            StringCchPrintf(ver, 64, TEXT("%d.%d.%d"), h2, m1, m2);
          StringCchPrintf(g_szVersion, MAX_VERSION_LENGTH, TEXT("iContact Avian Edition %s"), ver);
#ifdef BETA
          StringCchCat(g_szVersion, MAX_VERSION_LENGTH, TEXT(" beta"));
#endif
#ifdef LOG_ENABLED
          StringCchCat(g_szVersion, MAX_VERSION_LENGTH, TEXT(" LOG"));
#endif
          g_nVersionLen = ::_tcslen(g_szVersion);
          result = true;
        }
      }

      free(pData);
    }
  }

  return result;
}

//-----------------------------------------------------------------------------
// InitInstance - Instance initialization
//
HWND InitInstance (HINSTANCE hInstance, LPWSTR lpCmdLine, int nCmdShow)
{
#ifdef LOG_ENABLED
  LOGTIMER_START
#endif

    WNDCLASS wc;
  HWND hWnd;

  // Save program instance handle in global variable.
  hInst = hInstance;

#if defined(WIN32_PLATFORM_PSPC) || defined(WIN32_PLATFORM_WFSP)
  // If Windows Mobile, allow only one instance of the application.
  hWnd = FindWindow (szAppName, NULL);
  if (hWnd)
  {
    ApplyIDialerCommand(hWnd, lpCmdLine);
    SetForegroundWindow ((HWND)(((DWORD)hWnd) | 0x01));
    return 0;
  }
#endif

  FindVersion();

  // Register application main window class.
  wc.style = 0;                             // Window style
  wc.lpfnWndProc = MainWndProc;             // Callback function
  wc.cbClsExtra = 0;                        // Extra class data
  wc.cbWndExtra = 0;                        // Extra window data
  wc.hInstance = hInstance;                 // Owner handle
  wc.hIcon = NULL;                          // Application icon
  wc.hCursor = LoadCursor (NULL, IDC_ARROW);// Default cursor
  wc.hbrBackground = (HBRUSH) GetStockObject (BLACK_BRUSH);
  wc.lpszMenuName =  NULL;                  // Menu name
  wc.lpszClassName = szAppName;             // Window class name

  if (RegisterClass (&wc) == 0) return 0;
  // Create main window.
  hWnd = CreateWindowEx (WS_EX_NODRAG,      // Ex Style
    szAppName,           // Window class
    szAppName,           // Window title
    WS_SYSMENU,          // Style flags
    CW_USEDEFAULT,       // x position
    CW_USEDEFAULT,       // y position
    CW_USEDEFAULT,       // Initial width
    CW_USEDEFAULT,       // Initial height
    NULL,                // Parent
    NULL,                // Menu, must be null
    hInstance,           // Application instance
    NULL);               // Pointer to create
  // parameters
  // Return fail code if window not created.
  if (!IsWindow (hWnd)) return 0;

#ifdef LOG_ENABLED
  LOGTIMER_LOG("Startup Timer: Create window")
    LOGTIMER_UPDATE
#endif

    bInitializing = true;

  // Initialize COM libraries (for POOM)
  if (FAILED(CoInitializeEx(NULL, 0)))
    return 0;

#ifdef LOG_ENABLED
  LOGTIMER_LOG("Startup Timer: CoInitializeEx")
    LOGTIMER_UPDATE
#endif

    // Initialize POlApp
    EdbData::Initialize(hWnd);
  PoomData::Initialize(hWnd);

#ifdef LOG_ENABLED
  LOGTIMER_LOG("Startup Timer: PoomData")
    LOGTIMER_UPDATE
#endif

    // Initialize settings
    IniReader::Create();
  Settings::Create(hWnd);
  Language::Create(hWnd);

#ifdef LOG_ENABLED
  LOGTIMER_LOG("Startup Timer: Settings")
    LOGTIMER_UPDATE
#endif

    Theme::Create(hWnd);

#ifdef LOG_ENABLED
  LOGTIMER_LOG("Startup Timer: Theme")
    LOGTIMER_UPDATE
#endif

    ApplyCommandLine(lpCmdLine);
  ApplySettings();

  if (Settings::showPicturesInList || Settings::showPicturesInCallHistory)
    PoomPictures::Initialize(hWnd, true, Settings::delayedPictureLoading, Settings::enableListCaching);

  if (Settings::showSimContacts)
    SimManager::Initialize(hWnd, true, Settings::delayedSimLoading);

  SearchAlphabetMgr::Initialize();

#ifdef LOG_ENABLED
  LOGTIMER_LOG("Startup Timer: Pictures")
    LOGTIMER_UPDATE
#endif

    // Initialize titlebar callbacks
    if (!Settings::showSystemTaskbar)
      Titlebar::Initialize(hWnd);

#ifdef LOG_ENABLED
  LOGTIMER_LOG("Startup Timer: Titlebar")
    LOGTIMER_UPDATE
#endif

  pListMenu = new ListMenu(hWnd, nCurrentTab, CallLog_ShownType);
  pDetailsMenu = new DetailsMenu(hWnd);
  pSimDetailsMenu = new SimDetailsMenu(hWnd);
  pCallDetailsMenu = new CallDetailsMenu(hWnd);

  pSearchKbd = new SearchKbd(hWnd, Settings::t9SearchTransparency);
  pSearchBox = new ImeEditBox(IDC_SEARCH_EDIT_BOX, hWnd, hInstance, false);

  if (pItemDetails != NULL)
    delete pItemDetails;
  pItemDetails = new CListDetails(hWnd);

#ifdef LOG_ENABLED
  LOGTIMER_LOG("Startup Timer: Menues and Group")
    LOGTIMER_UPDATE
#endif

    // Standard show and update calls
    ShowWindow(hWnd, nCmdShow);
  UpdateWindow(hWnd);

#ifdef LOG_ENABLED
  LOGTIMER_LOG("Startup Timer: ShowWindow")
    LOGTIMER_UPDATE
#endif

    bool res = PoomData::SubscribeToNotifications(olFolderContacts, PIMFOLDERNOTIFICATION_ALL);
#ifdef LOG_ENABLED
  LOGTIMER_LOG("Startup Timer: PoomApp Notifications")
    LOGTIMER_UPDATE
#endif

    ApplyIDialerCommand(hWnd, lpCmdLine);

  SwitchTab(hWnd, nCurrentTab);
  pListData->Redraw();

#ifdef LOG_ENABLED
  LOGTIMER_LOG("Startup Timer: SwitchTab & Redraw")
    LOGTIMER_UPDATE
#endif

    if (Settings::delayedPictureLoading)
      PoomPictures::BeginLoading();
  if (Settings::delayedSimLoading)
    SimManager::BeginLoading();

#ifdef LOG_ENABLED
  LOGTIMER_LOG("Startup Timer: Pictures::BeginLoading()")
    LOGTIMER_UPDATE
#endif

    bInitializing = false;

  return hWnd;
}

//-----------------------------------------------------------------------------
// TermInstance - Program cleanup
//
int TermInstance (HINSTANCE hInstance, int nDefRC)
{
  if (pListDataFavorites)
    delete pListDataFavorites;
  if (pListDataCallLog)
    delete pListDataCallLog;
  if (pListDataContacts)
    delete pListDataContacts;
  if (pListCategories)
    delete pListCategories;
  if (pListMenu)
    delete pListMenu;
  if (pDetailsMenu)
    delete pDetailsMenu;
  if (pSimDetailsMenu)
    delete pSimDetailsMenu;
  if (pCallDetailsMenu)
    delete pCallDetailsMenu;
  if (pItemDetails)
    delete pItemDetails;

  if (pSearchKbd)
    delete pSearchKbd;
  if (pSearchBox)
    delete pSearchBox;

  if (hdcMem)
    DeleteDC(hdcMem);
  if (hbmMem)
    DeleteObject(hbmMem);
  if (hdcTmp)
    DeleteDC(hdcTmp);
  if (hbmTmp)
    DeleteObject(hbmTmp);
  if (hdcPage1)
    DeleteDC(hdcPage1);
  if (hbmPage1)
    DeleteObject(hbmPage1);
  if (hdcPage2)
    DeleteDC(hdcPage2);
  if (hbmPage2)
    DeleteObject(hbmPage2);

  if (Settings::showSimContacts)
    SimManager::Terminate();
  if (Settings::showPicturesInList || Settings::showPicturesInCallHistory)
    PoomPictures::Terminate();

  SearchAlphabetMgr::Terminate();

  //unsetHHook();

  if (!Settings::showSystemTaskbar)
    Titlebar::Terminate();

  Theme::Destroy();
  Language::Destroy();
  Settings::Destroy();
  IniReader::Destroy();

  PoomData::Terminate();
  EdbData::Terminate();

  return nDefRC;
}

void ApplySettings()
{
  if (Settings::openIn >= 0 && Settings::openIn < tabCount)
    nCurrentTab = (EMainTabs)Settings::openIn;

  if (Settings::openHistoryIn >= 0 && Settings::openHistoryIn < ctCount)
    CallLog_ShownType = (CallType)Settings::openHistoryIn;
}

void ApplyCommandLine(LPWSTR cmdLine)
{
  TCHAR* option;
  TCHAR* value;
  TCHAR* cur = cmdLine;
  while (*cur != 0)
  {
    if (*cur == TEXT('-'))
    {
      // get option
      cur = GetCommandLineOption(cur + 1, option, value);
      if (option != NULL)
      {
        if (_tcsicmp(option, TEXT("OpenIn")) == 0 && value != NULL)
        {
          if (_tcscmp(value, TEXT("1")) == 0)
            Settings::openIn = tabFavorites;
          else if (_tcscmp(value, TEXT("2")) == 0)
            Settings::openIn = tabCallLog;
          else if (_tcscmp(value, TEXT("3")) == 0)
            Settings::openIn = tabContacts;
        }
        else if (_tcsicmp(option, TEXT("OpenHistoryIn")) == 0 && value != NULL)
        {
          if (_tcscmp(value, TEXT("1")) == 0)
            Settings::openHistoryIn = ctAll;
          else if (_tcscmp(value, TEXT("2")) == 0)
            Settings::openHistoryIn = ctMissed;
          else if (_tcscmp(value, TEXT("3")) == 0)
            Settings::openHistoryIn = ctIncoming;
          else if (_tcscmp(value, TEXT("4")) == 0)
            Settings::openHistoryIn = ctOutgoing;
        }
        else if (_tcsicmp(option, TEXT("FavoritesCat")) == 0 && value != NULL)
        {
          if (value[0] != 0)
          {
            Settings::favorite_category = value;
          }
        }
        else if (_tcsicmp(option, TEXT("ShowSimContacts")) == 0 && value != NULL)
        {
          Settings::showSimContacts = (_tcscmp(value, TEXT("1")) == 0);
        }
      }
    }
    else
    {
      cur++;
    }
  }
}

void ApplyIDialerCommand(HWND hWnd, LPWSTR cmdLine)
{
  TCHAR* option;
  TCHAR* value;
  TCHAR* cur = cmdLine;
  while (*cur != 0)
  {
    if (*cur == TEXT('-'))
    {
      // get option
      cur = GetCommandLineOption(cur + 1, option, value);
      if (option != NULL)
      {
        // iDialer compatibility
        if (_tcsicmp(option, TEXT("favorites")) == 0)
        {
          Settings::openIn = tabFavorites;
          PostCommand(hWnd, CMD_LIST_FAVORITES);
        }
        else if (_tcsicmp(option, TEXT("recents")) == 0)
        {
          Settings::openIn = tabCallLog;
          PostCommand(hWnd, CMD_LIST_CALLLOG);
        }
        else if (_tcsicmp(option, TEXT("contacts")) == 0)
        {
          Settings::openIn = tabContacts;
          PostCommand(hWnd, CMD_LIST_CONTACTS);
        }
        else if (_tcsicmp(option, TEXT("dialer")) == 0)
        {
          PostCommand(hWnd, CMD_LIST_DIALER);
        }
        else if (_tcsicmp(option, TEXT("search")) == 0)
        {
          Settings::openIn = tabContacts;
          PostCommand(hWnd, CMD_LIST_KEYBOARD);
        }
      }
    }
    else
    {
      cur++;
    }
  }
}

TCHAR* GetCommandLineOption(TCHAR* optionPtr, TCHAR* &name, TCHAR* &value)
{
  name = optionPtr;
  value = NULL;
  TCHAR* cur = optionPtr;
  bool quotes = false;
  while (*cur != 0)
  {
    if (value == NULL)
    {
      if (*cur == TEXT('='))
      {
        *cur = 0;
        value = cur + 1;
      }
      else if (_istspace(*cur))
      {
        *cur = 0;
        cur++;
        break;
      }
    }
    else
    {
      if (*cur == TEXT('\"'))
      {
        if (!quotes)
        {
          quotes = true;
          value = cur + 1;
        }
        else
        {
          *cur = 0;
          cur++;
          break;
        }
      }
      else if (!quotes && _istspace(*cur))
      {
        *cur = 0;
        cur++;
        break;
      }
    }
    cur++;
  }
  return cur;
}

//=============================================================================
// Message handling procedures for MainWindow
//

//-----------------------------------------------------------------------------
// MainWndProc - Callback function for application window
//
LRESULT CALLBACK MainWndProc (HWND hWnd, UINT wMsg, WPARAM wParam, 
                              LPARAM lParam)
{
  //#ifdef LOG_ENABLED
  //  wchar_t str[200];
  //  wsprintf(str, L"**MAINWNDPROC : %x", wMsg);
  //  LOGTIMER_START
  //  LOGTIMER_WLOG(str)
  //  LOGTIMER_UPDATE
  //#endif


  //
  // Search message list to see if we need to handle this
  // message. If in list, call procedure.
  //
  for (INT i = 0; i < dim(MainMessages); i++)
  {
    if (wMsg == MainMessages[i].Code)
      return (*MainMessages[i].Fxn)(hWnd, wMsg, wParam, lParam);
  }
  return DefWindowProc (hWnd, wMsg, wParam, lParam);
}

//-----------------------------------------------------------------------------
// DoPaintMain - Process WM_PAINT message for window.
//
LRESULT DoPaintMain (HWND hWnd, UINT wMsg, WPARAM wParam, 
                     LPARAM lParam)
{
  PAINTSTRUCT ps;
  HDC hdc;

  hdc = BeginPaint (hWnd, &ps); 

  // Draw empty screen while initializing
  if (bInitializing)
  {
    FillRect(hdc, &rScreen, Theme::hbrListBackground);
    if (!Settings::showSystemTaskbar)
      Titlebar::Draw(hdc, rTitlebar);
    EndPaint (hWnd, &ps);
    return 0;
  }

  // rect is the region that needs to be painted
  RECT rect = ps.rcPaint;
  int rScreenWidth = rScreen.right - rScreen.left;
  int rScreenHeight = rScreen.bottom - rScreen.top;   

  if (bTransitioning)
  {
    DrawTransition(hdc, trTransitionType);
  }
  // not transitioning
  else
  {
    if (PopupShown)
    {
      bool alpha = Settings::popupAlpha;
      if (PopupWindow == pwCategories && pListCategories != NULL)
      {
        alpha &= !Settings::optimizedAlpha || !pListCategories->IsMoving();

        if (!alpha && pListCategories->GetHeight() < rScreenHeight)
          BitBlt(hdcMem, rScreen.left, rScreen.top, rScreenWidth, rScreenHeight, hdcPage1, 0, 0, SRCCOPY);
      }
      if (alpha)
        BitBlt(hdcMem, rScreen.left, rScreen.top, rScreenWidth, rScreenHeight, hdcPage1, 0, 0, SRCCOPY);

      switch (PopupWindow)
      {
      case pwKeyboard:
        // draw the keyboard
        DrawKeyboardOn(hdcTmp, rPopup);
        break;

      case pwCategories:
        // draw categories
        if (pListCategories)
          pListCategories->Draw(hdcTmp, hdcTmp, rPopup);
        break;
      }

      if (alpha)
        GraphicFunctions::BltAlpha(hdcMem, rPopup.left, rPopup.top, rPopup.right-rPopup.left, rPopup.bottom-rPopup.top,
        hdcTmp, 220);
      else
        BitBlt(hdcMem, rPopup.left, rPopup.top, rPopup.right-rPopup.left, rPopup.bottom-rPopup.top,
        hdcTmp, rPopup.left, rPopup.top, SRCCOPY);
    }
    else
    {
      DrawScreenOn(hdcMem, CurrentScreenMode, hdcTmp, rect);
    }

    if (SearchKbdShown)
    {
      if (pSearchKbd->IsTransparent())
      {
        if (!Settings::optimizedAlpha || !pListData->IsMoving())
        {
          pSearchKbd->Draw(hdcTmp, rect);
          GraphicFunctions::BltAlpha(hdcMem,
            pSearchKbd->GetRect().left, pSearchKbd->GetRect().top, pSearchKbd->GetWidth(), pSearchKbd->GetHeight(),
            hdcTmp, 220);
        }
      }
      else
      {
        pSearchKbd->Draw(hdcMem, rect);
      }
    }
  }

  // draw thread busy incicator
  int i = GetBackgroundTasks();
  if (i != bgtNone)
  {
    ::SetPixel(hdcMem, rScreen.right - 10, rTitlebar.bottom + 5, Theme::rgbListScrollbarIndicator);
    ::SetPixel(hdcMem, rScreen.right -  8, rTitlebar.bottom + 5, Theme::rgbListScrollbarIndicator);
    ::SetPixel(hdcMem, rScreen.right -  6, rTitlebar.bottom + 5, Theme::rgbListScrollbarIndicator);
  }

  // Transfer everything to the actual screen
  BitBlt(hdc, rect.left, rect.top, rect.right - rect.left,
    rect.bottom - rect.top, hdcMem, rect.left, rect.top, SRCCOPY);

  EndPaint (hWnd, &ps);

  return 0;
}
//-----------------------------------------------------------------------------
// DoActivate - Process WM_ACTIVATE message for window
//
LRESULT DoActivate (HWND hWnd, UINT wMsg, WPARAM wParam,
                    LPARAM lParam)
{
  DWORD dwState;
  RECT rc;

  if (wParam == WA_CLICKACTIVE || wParam == WA_ACTIVE)
  {
    // To switch to full screen mode, first hide all of the shell parts.
    if (Settings::showSystemTaskbar)
      dwState = (SHFS_SHOWTASKBAR | SHFS_SHOWSTARTICON | SHFS_HIDESIPBUTTON);
    else
      dwState = (SHFS_HIDETASKBAR | SHFS_HIDESTARTICON | SHFS_HIDESIPBUTTON);

    SHFullScreen(hWnd, dwState);

    if (dwState & SHFS_SHOWTASKBAR)
    {
      // Resize the main window to the size of the work area.
      SystemParametersInfo(SPI_GETWORKAREA, 0, &rc, FALSE);
      if (Settings::IsSmartphone())
      {
        // Overlap menubar
        rc.bottom = ::GetSystemMetrics(SM_CYSCREEN);
      }
    }
    else
    {
      // Resize the main window to the size of the screen.
      SetRect(&rc, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
    }

    MoveWindow(hWnd, rc.left, rc.top, rc.right-rc.left, rc.bottom-rc.top, TRUE);

    ShowWindow(hWnd, SW_SHOWNORMAL);

    // Hide SIP
    SipShowIM(SIPF_OFF);
    ShowWindow(FindWindow(L"MS_SIPBUTTON", NULL), SW_HIDE);
    KillTimer(hWnd, IDT_TIMER_HIDE_SIP);
    SetTimer(hWnd, IDT_TIMER_HIDE_SIP, 2000, NULL);

    DoActivated(hWnd);
  }
  // The window is being deactivated... restore it to non-fullscreen
  else if (!::IsChild(hWnd, (HWND)lParam))
  {
    // Kill the "hide sip" timer, if it's running
    KillTimer(hWnd, IDT_TIMER_HIDE_SIP);

    // To switch to normal mode, first show all of the shell parts.
    dwState = (SHFS_SHOWTASKBAR | SHFS_SHOWSTARTICON | SHFS_SHOWSIPBUTTON);

    SHFullScreen(hWnd, dwState);

    // Next resize the main window to the size of the work area.
    SystemParametersInfo(SPI_GETWORKAREA, 0, &rc, FALSE);

    MoveWindow(hWnd, rc.left, rc.top, rc.right-rc.left,
      rc.bottom-rc.top, TRUE);

    DoDeactivated(hWnd);
  }

  return DefWindowProc (hWnd, wMsg, wParam, lParam);
}

void DoActivated(HWND hWnd)
{
  if (nCurrentTab == tabCallLog && (CallLog_ShownType == ctAll || CallLog_ShownType == ctMissed))
  {
    PhoneUtils::ResetMissedCalls();
  }
}

void DoDeactivated(HWND hWnd)
{
}

//-----------------------------------------------------------------------------
// DoSize - Process WM_SIZE message for window
//
LRESULT DoSize (HWND hWnd, UINT wMsg, WPARAM wParam,
                LPARAM lParam)
{
  if (wParam == SIZE_MINIMIZED)
  {
    if (Settings::showSystemTaskbar && Settings::doExitOnMinimize)
    {
      DestroyWindow(hWnd);
      return 0;
    }
  }
  else
  {
    InitSurface(hWnd);
    //CalculateKeyboard();

    if (CurrentScreenMode == smDetails && pListData)
    {
      PopulateDetailsInfo(hWnd, pListData->GetSelection());
    }
  }

  return DefWindowProc (hWnd, wMsg, wParam, lParam);
}

//-----------------------------------------------------------------------------
// DoLButtonDown - Process WM_LBUTTONDOWN message for window
//
LRESULT DoLButtonDown (HWND hWnd, UINT wMsg, WPARAM wParam,
                       LPARAM lParam)
{
  bIsMouseDown = true;
  bIgnoreNextMouseUp = false;

  if (bTransitioning)
  {
    return 0;
  }

  MouseActionEnabled = true;
  SkipMouseAction = false;

  if (lParam >= 0)  // < 0 indicates we are in the windows taskbar, so don't update the x/y
  {
    curMousePosX = LOWORD(lParam);
    curMousePosY = HIWORD(lParam);
  }

  StartY = curMousePosY;

  //burt: mouse gestures begin
  if (Settings::enableMouseGestures)
  {
    MouseGesture_On = false;
    MouseGesture_StartTime = ::GetTickCount();
    MouseMove_dx = 0;
    MouseMove_dy = 0;
    MouseMove_LastX = curMousePosX;
    MouseMove_LastY = curMousePosY;
  }
  //burt: mouse gestures end

  if (PopupShown)
  {
    MouseArea = maPopup;
    DoPopupLButtonDown(hWnd, curMousePosX, curMousePosY);
  }
  else if (SearchKbdShown && pSearchKbd->IsPtInRect(curMousePosX, curMousePosY))
  {
    MouseArea = maSearchKbd;
    pSearchKbd->OnMouseDown(curMousePosX, curMousePosY);
  }
  else if (curMousePosY < rTitlebar.bottom)
  {
    MouseArea = maTitlebar;
  }
  else if (pBottomMenu != NULL && curMousePosY > rMenubar.top)
  {
    MouseArea = maMenubar;
    pBottomMenu->OnMouseDown(curMousePosX, curMousePosY);
  }
  else
  {
    switch (CurrentScreenMode)
    {
    case smList:
      MouseArea = maList;
      if (pListData)
        pListData->OnMouseDown(curMousePosX, curMousePosY);
      break;
    case smDetails:
      MouseArea = maDetails;
      if (pItemDetails)
        pItemDetails->OnMouseDown(curMousePosX, curMousePosY);
      break;
    }
  }

  // Capture mouse events that happen outside of the window (in case we are using Windows Mobile Taskbar)
  SetCapture(hWnd);

  return 0;
}

// DoMouseMove - Process WM_MOUSEMOVE message for window
//
LRESULT DoMouseMove (HWND hWnd, UINT wMsg, WPARAM wParam,
                     LPARAM lParam)
{
  bIsMouseDown = true;

  if (bTransitioning)
  {
    return 0;
  }

  if (!bIgnoreNextMouseUp)
  {
    if (lParam >= 0)  // < 0 indicates we are in the windows taskbar, so don't update the x/y
    {
      curMousePosX = LOWORD(lParam);
      curMousePosY = HIWORD(lParam);
    }

    if (!MouseActionEnabled)
      return 0;

    //burt: mouse gestures begin
    if (Settings::enableMouseGestures)
    {
      int dx = curMousePosX - MouseMove_LastX;
      int dy = curMousePosY - MouseMove_LastY;
      if (dx != 0 || dy != 0)
      {
        MouseGesture_On = true;
        MouseMove_dx += dx;
        MouseMove_dy += dy;
        MouseMove_LastX = curMousePosX;
        MouseMove_LastY = curMousePosY;
      }
    }
    //burt: mouse gestures end

    switch(MouseArea)
    {
    case maList:
      if (pListData)
        pListData->OnMouseMove(curMousePosX, curMousePosY);
      break;
    case maDetails:
      if (pItemDetails)
        pItemDetails->OnMouseMove(curMousePosX, curMousePosY);
      break;
    case maTitlebar:
      break;
    case maMenubar:
      if (pBottomMenu)
        pBottomMenu->OnMouseMove(curMousePosX, curMousePosY);
      break;
    case maPopup:
      DoPopupMouseMove(hWnd, curMousePosX, curMousePosY);
      break;
    case maSearchKbd:
      pSearchKbd->OnMouseMove(curMousePosX, curMousePosY);
      break;
    }
  }

  return 0;
}

//-----------------------------------------------------------------------------
// DoLButtonUp - Process WM_LBUTTONUP message for window
//
LRESULT DoLButtonUp (HWND hWnd, UINT wMsg, WPARAM wParam,
                     LPARAM lParam)
{
  if (bIgnoreNextMouseUp && bIsMouseDown)
  {
    bIgnoreNextMouseUp = false;
    bIsMouseDown = false;
    return 0;
  }

  bIgnoreNextMouseUp = false;
  bIsMouseDown = false;

  if (bTransitioning)
  {
    return 0;
  }

  if (lParam >= 0)  // < 0 indicates we are in the windows taskbar, so don't update the x/y
  {
    curMousePosX = LOWORD(lParam);
    curMousePosY = HIWORD(lParam);
  }

  // Release any previously set mouse capture (for outside the window)
  ReleaseCapture();

  if (!MouseActionEnabled)
  {
    MouseActionEnabled = true;
    return 0;
  }

  //burt: mouse gestures begin
  if (MouseGesture_On)
  {
    bool result = false;
    if ((::GetTickCount() - MouseGesture_StartTime) <= (DWORD)Settings::mouseGesturesTime)
    {
      // return true to skip further processing, false to continue standard processing
      // !!! Make sure that you take care of bScrolling and other flags 
      //     which should be finished here, otherwise return false !!!
      result = DoMouseGesture(hWnd, MouseMove_dx, MouseMove_dy);
    }
    MouseGesture_On = false;
    MouseMove_dx = 0;
    MouseMove_dy = 0;
    MouseMove_LastX = 0;
    MouseMove_LastY = 0;
    if (result)
      return 0;
  }
  //burt: mouse gestures end

  switch(MouseArea)
  {
  case maList:
    if (pListData)
      pListData->OnMouseUp(curMousePosX, curMousePosY);
    break;
  case maDetails:
    if (pItemDetails)
      pItemDetails->OnMouseUp(curMousePosX, curMousePosY);
    break;
  case maTitlebar:
    if (curMousePosY < rTitlebar.bottom)
      DoTitlebarClick(hWnd, curMousePosX, curMousePosY);
    break;
  case maMenubar:
    if (pBottomMenu != NULL)
      pBottomMenu->OnMouseUp(curMousePosX, curMousePosY);
    break;
  case maPopup:
    DoPopupLButtonUp(hWnd, curMousePosX, curMousePosY);
    break;
  case maSearchKbd:
    pSearchKbd->OnMouseUp(curMousePosX, curMousePosY);
    break;
  }

  return 0;
}

//-----------------------------------------------------------------------------
// DoTimer - Process WM_TIMER message for window
//
LRESULT DoTimer (HWND hWnd, UINT wMsg, WPARAM wParam,
                 LPARAM lParam)
{
  switch (wParam)
  {
  case IDT_TIMER_TRANSITION:
    dTransitionPct = (double)(::GetTickCount() - dwTransitionStart) / nTransitionDuration;

    if (dTransitionPct >= 1.0)
    {
      KillTimer(hWnd, IDT_TIMER_TRANSITION);

      dTransitionPct = 1.0;
      TransitionFinished(hWnd);
    }
    InvalidateRect(hWnd, &rScreen, FALSE);
    break;

  case IDT_TIMER_RELOAD:
    DoDBNotificationTimer();
    break;

  case IDT_TIMER_HIDE_SIP:
    KillTimer(hWnd, IDT_TIMER_HIDE_SIP);
    SipShowIM(SIPF_OFF);
    ShowWindow(FindWindow(L"MS_SIPBUTTON", NULL), SW_HIDE);
    break;
  }

  return 0;
}

//-----------------------------------------------------------------------------
// DoKeyDown - Process WM_KEYDOWN message for window
//
LRESULT DoKeyDown (HWND hWnd, UINT wMsg, WPARAM wParam,
                   LPARAM lParam)
{
#ifdef LOG_ENABLED
  Logger::LogFmt(TEXT("key: %lu"), wParam);
#endif

  lastKey = (int)wParam;

  if (bTransitioning)
    return 0;

  bool bRepeating = (lParam & (1 << 30)) != 0;

  switch (CurrentScreenMode)
  {
  case smList:
    if (PopupShown)
    {
      return 0;
    }

    if (bRepeating)
    {
      nKeyRepeatCount++;
      if (nKeyRepeatCount < 10)
        bRepeating = false;
    }
    else
    {
      nKeyRepeatCount = 0;
    }

    switch (wParam)
    {
    case VK_LEFT:
      if (SearchKbdShown)
      {
        pSearchKbd->AddKey(skkBackspace);
      }
      else if (nCurrentTab > 0)
      {
        SwitchTab(hWnd, (EMainTabs)(nCurrentTab - 1));
        pListData->Redraw();

      }
      break;

    case VK_RIGHT:
      if (SearchKbdShown)
      {
        pSearchKbd->Close();
      }
      else if (nCurrentTab + 1 < tabCount)
      {
        SwitchTab(hWnd, (EMainTabs)(nCurrentTab + 1));
        pListData->Redraw();
      }
      break;

    case VK_TSOFT1:
    case VK_TSTAR:
      if (SearchKbdShown)
      {
        pSearchKbd->ToggleMode();
      }
      break;

    case VK_TSOFT2:
    case VK_TPOUND:
      if (SearchKbdShown)
      {
        if (pSearchKbd->GetFilterLength() == 0)
          pSearchKbd->Close();
        else
          pSearchKbd->AddKey(skkBackspace);
      }
      break;

    default:
      if (pListData)
        pListData->OnKeyDown(wParam, bRepeating);
      break;
    }
    break;

  case smDetails:
    if (pItemDetails)
      pItemDetails->OnKeyDown(wParam, bRepeating);
    break;
  }

  return DefWindowProc (hWnd, wMsg, wParam, lParam);
}

//-----------------------------------------------------------------------------
// DoKeyUp - Process WM_KEYUP message for window
//
LRESULT DoKeyUp (HWND hWnd, UINT wMsg, WPARAM wParam,
                 LPARAM lParam)
{
#ifdef LOG_ENABLED
  Logger::LogFmt(TEXT("keyup: %lu"), wParam);
#endif

  nKeyRepeatCount = 0;

  switch(wParam)
  {
  case VK_TBACK:
    if (DoKeyHook(hWnd, wParam, lParam))
      return 0;

  case VK_TEND:
  case VK_TTALK:
    if (Settings::enablePhoneKeyHook)
    {
      if (DoKeyHook(hWnd, wParam, lParam))
        return 0;
    }
  }

  if (SkipKeyboardAction)
  {
    SkipKeyboardAction = false;
    return 0;
  }

  if (bTransitioning)
    return 0;

  switch (CurrentScreenMode)
  {
  case smList:
    if (pListData)
      pListData->OnKeyUp(wParam);
    break;

  case smDetails:
    if (pItemDetails)
      pItemDetails->OnKeyUp(wParam);
    break;
  }

  return DefWindowProc (hWnd, wMsg, wParam, lParam);
}

//-----------------------------------------------------------------------------
// DoKeyHook - keyhook callback
//
bool DoKeyHook (HWND hWnd, DWORD nKey, DWORD nKeyData)
{
#ifdef LOG_ENABLED
  Logger::LogFmt(TEXT("key hook: %lu"), nKey);
#endif

  switch(nKey)
  {
  case VK_TTALK:
    if (Settings::enablePhoneKeyHook && PopupShown)
      break;

    switch(CurrentScreenMode)
    {
    case smList:
      ClearFilter(hWnd, true);
      if (DoDefaultAction(hWnd, pListData->GetSelection()))
        return true;
      break;

    case smDetails:
      {
        CListItem* item = pItemDetails->GetSel();
        if (item != NULL)
          item->OnMouseClick(hWnd, 0, 0);
        else
          DoDefaultAction(hWnd, pListData->GetSelection());
      }
      break;
    }
    break;

  case VK_TEND:
    if (Settings::enablePhoneKeyHook)
    {
      if (Settings::doExitOnMinimize)
        DestroyWindow(hWnd);
      else
        ShowWindow(hWnd, SW_MINIMIZE);
      return true;
    }

  case VK_TBACK:
    if (PopupShown)
    {
      StartTransition(hWnd, ttHidePopup, Settings::popupHideTime);
      return true;
    }
    if (CurrentScreenMode == smDetails)
    {
      OnCmdDetailsBack(hWnd, 0);
      return true;
    }
    else if (SearchKbdShown)
    {
      if ((nKeyData & 0xF) > 10 ||
        pSearchKbd->GetFilterLength() == 0)
        pSearchKbd->Close();
      else
        pSearchKbd->AddKey(skkBackspace);
      return true;
    }
    else if (pSearchBox && pSearchBox->IsVisible())
    {
      if ((nKeyData & 0xF) > 10 ||
        pSearchBox->GetTextLength() == 0)
      {
        ClearFilter(hWnd, true);
      }
      else
        pSearchBox->DoBackspace();      
      return true;
    }
  }

  return false;
}

//-----------------------------------------------------------------------------
// DoChar - Process WM_CHAR message for window
//
LRESULT DoChar (HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
{
  TCHAR c = (TCHAR)wParam;
#ifdef LOG_ENABLED
  Logger::LogFmt(TEXT("char: %c"), c);
#endif

  switch(CurrentScreenMode)
  {
  case smList:
    if (pSearchBox && _istalnum(c))
    {
      ClearFilter(hWnd, true);
      pSearchBox->Show(lastKey);
      return 0;
    }
    break;

  case smDetails:
    if (pBottomMenu != NULL)
    {
      int i = c - TEXT('1');
      if (i >= 0 && i < pBottomMenu->GetItemCount())
      {
        i = pBottomMenu->GetItemAt(i);
        i = pBottomMenu->GetItemCommand(i);
        if (i != CMD_NONE)
          PostCommand(hWnd, i);
      }
    }
    break;
  }

  return DefWindowProc (hWnd, wMsg, wParam, lParam);
}

#define BEGIN_COMMAND_HANDLER switch(LOWORD(wParam)) {
#define ON_COMMAND(id, func) case id: func(hWnd, HIWORD(wParam)); break;
#define END_COMMAND_HANDLER   }

LRESULT DoCommand(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
{
  switch(LOWORD(wParam))
  {
  case IDC_SEARCH_EDIT_BOX:
    if (HIWORD(wParam) == EN_CHANGE)
    {
      OnSearchBoxChange(hWnd);
      return 0;
    }
    break;
  }
  BEGIN_COMMAND_HANDLER
    ON_COMMAND(CMD_LIST_FAVORITES, OnCmdListFavorites)
    ON_COMMAND(CMD_LIST_CALLLOG,   OnCmdListCallLog)
    ON_COMMAND(CMD_LIST_CONTACTS,  OnCmdListContacts)
    ON_COMMAND(CMD_LIST_DIALER,    OnCmdListDialer)
    ON_COMMAND(CMD_LIST_KEYBOARD,  OnCmdListKeyboard)
    ON_COMMAND(CMD_DETAILS_BACK,    OnCmdDetailsBack)
    ON_COMMAND(CMD_DETAILS_VIEW,    OnCmdDetailsView)
    ON_COMMAND(CMD_DETAILS_EDIT,    OnCmdDetailsEdit)
    ON_COMMAND(CMD_DETAILS_DELETE,  OnCmdDetailsDelete)
    ON_COMMAND(CMD_DETAILS_FAVORITE,OnCmdDetailsFavorite)
    ON_COMMAND(CMD_DETAILS_CL_NEWCONTACT,  OnCmdDetailsClNewContact)
    ON_COMMAND(CMD_DETAILS_CL_GOTOCONTACT, OnCmdDetailsClGoToContact)
    ON_COMMAND(CMD_DETAILS_CL_DELETECALL,  OnCmdDetailsClDeleteCall)
    ON_COMMAND(CMD_DETAILS_SIM_NEWCONTACT, OnCmdDetailsSimNewContact)
    ON_COMMAND(CMD_DETAILS_SIM_GOTOOUTLOOK,OnCmdDetailsSimGoToOutlook)
    ON_COMMAND(CMD_DETAILS_SET_PICTURE,    OnCmdDetailsSetPicture)
    ON_COMMAND(CMD_DETAILS_SET_RINGTONE,   OnCmdDetailsSetRingtone)
    ON_COMMAND(CMD_LIST_SHOW_DETAILS,      OnCmdListShowDetails)
    ON_COMMAND(CMD_LIST_SET_CATEGORY,      OnCmdListSetCategory)
    ON_COMMAND(CMD_LIST_HIDE_CATEGORIES,   OnCmdListHideCategories)
  END_COMMAND_HANDLER

  return DefWindowProc (hWnd, wMsg, wParam, lParam);
}

LRESULT DoCtlColorEdit(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
{
  if (pSearchBox && (HWND)lParam == pSearchBox->GetWindow())
  {
    return pSearchBox->DoCtlColorEdit((HDC)wParam);
  }
  return DefWindowProc (hWnd, wMsg, wParam, lParam);
}

// Search Keyboard
LRESULT DoSkkStringUpdated(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  TCHAR match[SKK_MAX_STRING_LENGTH];
  pListData->ApplyFilter((const SearchKbdKeys*)wParam, (int)lParam, match);
  pSearchKbd->SetDisplayString(match);

  return 0;
}

LRESULT DoSkkModeChanged(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  CalculateRects();
  pListData->Redraw();    // Make sure the screen is properly refreshed
  pListData->DoBounce();  // Check to see if we need to bounce items back into view
  return 0;
}

LRESULT DoSkkClose(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  // to avoid menu flicking
  SetMenu(hWnd, pListMenu, false);
  ClearFilter(hWnd, false);
  StartTransition(hWnd, ttHideSkk, Settings::popupHideTime);
  return 0;
}

//-----------------------------------------------------------------------------
// DoDestroyMain - Process WM_DESTROY message for window
//
LRESULT DoDestroyMain (HWND hWnd, UINT wMsg, WPARAM wParam, 
                       LPARAM lParam)
{
  // Uninitialize the COM classes
  CoUninitialize();

  // Quit
  PostQuitMessage (0);
  return 0;
}

//-----------------------------------------------------------------------------
// DoCallInfoCallback - Process NM_CALLINFO message for window
//
LRESULT DoDbNotificationCallback (HWND hWnd, UINT wMsg, WPARAM wParam,
                                  LPARAM lParam)
{
#ifdef LOG_ENABLED
  Logger::Log(TEXT("DoDbNotificationCallback"));
#endif

  CENOTIFICATION* pNotify = (CENOTIFICATION*)lParam;
  if (pNotify)
  {
    switch(pNotify->uType)
    {
    case DB_CEOID_CREATED:
    case DB_CEOID_CHANGED:
    case DB_CEOID_RECORD_DELETED:
      if (pNotify->dwParam == EDB_CLOG)
      {
        if (!reloadBuffered)
        {
          lasthwnd = hWnd;
          SetTimer(hWnd, IDT_TIMER_RELOAD, RELOAD_TIMER_INTERVAL, NULL);
          reloadBuffered = true;
        }
      }
      break;
    }

    EdbData::FreeNotification(pNotify);
  }

  return 0;
}

void DoDBNotificationTimer()
{
  KillTimer(lasthwnd, IDT_TIMER_RELOAD);

  if (pListDataCallLog)
  {
    ReloadList(lasthwnd, tabCallLog, true);
    if (nCurrentTab == tabCallLog)
    {
      pListData->CalculateHeights();
      //CalculateKeyboard();
      if (CurrentScreenMode == smDetails)
        ExpandDetails(lasthwnd);
      else
        pListData->Redraw();
    }
  }

  reloadBuffered = false;
}

//-----------------------------------------------------------------------------
// DoTitlebarCallback - Process NM_TITLEBAR message for window
//
LRESULT DoTitlebarCallback (HWND hWnd, UINT wMsg, WPARAM wParam,
                            LPARAM lParam)
{
  Titlebar::Refresh(lParam);
  InvalidateRect(hWnd, &rTitlebar, false);
  return 0;
}

LRESULT DoPoomContactCreated(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
{
  CEOID oid = (CEOID)wParam;
#ifdef LOG_ENABLED
  Logger::LogFmt(TEXT("DoPoomContactCreated: %lu"), oid);
#endif

  if (pListDataFavorites)
    ReloadList(hWnd, tabFavorites);
  if (pListDataContacts)
    ReloadList(hWnd, tabContacts);

  if (nCurrentTab != tabCallLog)
  {
    pListData->CalculateHeights();
    //CalculateKeyboard();
    if (CurrentScreenMode == smDetails)
      ExpandDetails(hWnd);
    else
      pListData->Redraw();
  }
  // Notify CallLog of item update
  if (pListDataCallLog)
  {
    pListDataCallLog->OutlookContactChanged(oid);
    
    // it might be item for current item, refresh it
    if (nCurrentTab == tabCallLog && CurrentScreenMode == smDetails && pItemDetails->GetOid() == 0)
    {
      PopulateDetailsInfo(hWnd, pListData->GetSelection());
      ToggleMenu(hWnd, mtCallDetails);
      pListData->Redraw();
    }

    ReloadList(hWnd, tabCallLog);
  }

  return 0;
}

LRESULT DoPoomContactChanged(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
{
  CEOID oid = (CEOID)wParam;
  int  idxContacts = pListDataContacts ? pListDataContacts->GetItemIdxByOid(oid) : -1;
  int  idxFavs = pListDataFavorites ? pListDataFavorites->GetItemIdxByOid(oid) : -1;

  bool reloadContacts = false;
  bool reloadFavs = false;

  // check if item name has been changed
  // if it has, then we need to reload the list completely,
  // since item can be moved up or down inside the list
  IContact* pContact = NULL;
  if (SUCCEEDED(PoomData::GetApp()->GetItemFromOid((long)oid, reinterpret_cast<IDispatch**>(&pContact))))
  {
    const TCHAR* szContactName = idxContacts >= 0 ?
      pListDataContacts->GetItem(idxContacts)->szPrimaryText :
    (idxFavs >= 0 ?
      pListDataFavorites->GetItem(idxFavs)->szPrimaryText :
    NULL);
    BSTR bstr;
    if (szContactName && SUCCEEDED(pContact->get_FileAs(&bstr)))
    {
      if (_tcscmp(szContactName, bstr) != 0)
      {
        if (idxContacts >= 0)
          reloadContacts = true;
        if (idxFavs >= 0)
          reloadFavs = true;
      }
    }
    SysFreeString(bstr);
    // check category change for Favorites tab
    if (pListDataFavorites && !reloadFavs && SUCCEEDED(pContact->get_Categories(&bstr)))
    {
      // check if category has been removed or added
      TCHAR* found = _tcsstr(bstr, pListDataFavorites->GetCategory());
      // cat found != favs found
      reloadFavs = (found != NULL) != (idxFavs >= 0);
      SysFreeString(bstr);
    }
    pContact->Release();
  }

  if (reloadContacts || reloadFavs)
  {
#ifdef LOG_ENABLED
    LOGTIMER_START
#endif
    if (pListDataFavorites && reloadFavs)
      ReloadList(hWnd, tabFavorites);
    if (pListDataContacts && reloadContacts)
      ReloadList(hWnd, tabContacts);

#ifdef LOG_ENABLED
    LOGTIMER_LOG("DoPoomContactChanged: reload lists");
#endif

    if ((reloadContacts && nCurrentTab == tabContacts) || (reloadFavs && nCurrentTab == tabFavorites))
    {
      pListData->CalculateHeights();
      //CalculateKeyboard();
      if (CurrentScreenMode == smDetails)
        ReloadAfterDetails(hWnd);
      else
        pListData->Redraw();
    }
    // Notify CallLog of item update on full reload
    if (pListDataCallLog)
    {
      if (nCurrentTab == tabCallLog)
      {
        if (CurrentScreenMode == smDetails)
        {
          if (pListDataCallLog->GetItemIdxByOid(oid) == pListData->GetSelection())
          {
            PopulateDetailsInfo(hWnd, pListData->GetSelection());
            ToggleMenu(hWnd, mtCallDetails);
          }
        }
        else
        {
          pListData->CalculateHeights();
        }

        pListData->Redraw();
      }
    }
  }
  else
  {
#ifdef LOG_ENABLED
    LOGTIMER_START
#endif
    if (pListDataFavorites)
      pListDataFavorites->ReloadItem(oid);
    if (pListDataContacts)
      pListDataContacts->ReloadItem(oid);
#ifdef LOG_ENABLED
    LOGTIMER_LOG("DoPoomContactChanged: reload item");
#endif

    if (nCurrentTab != tabCallLog)
    {
      if (CurrentScreenMode == smDetails &&
        pListData->GetItemIdxByOid(oid) == pListData->GetSelection())
      {
        PopulateDetailsInfo(hWnd, pListData->GetSelection());
        pListData->Redraw();
      }
      else if (CurrentScreenMode == smList)
      {
        pListData->Redraw();
      }
    }
  }

  return 0;
}

LRESULT DoPoomContactDeleted(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
{
  CEOID oid = (CEOID)wParam;

#ifdef LOG_ENABLED
  Logger::LogFmt(TEXT("DoPoomContactDeleted: %lu"), oid);
#endif

  if (pListDataFavorites && pListDataFavorites->GetItemIdxByOid(oid) != -1)
    ReloadList(hWnd, tabFavorites);
  if (pListDataContacts)
    ReloadList(hWnd, tabContacts);
  if (pListDataCallLog)
    ReloadList(hWnd, tabCallLog);

  PoomPictures::Remove(oid);

  if (nCurrentTab != tabCallLog)
  {
    pListData->CalculateHeights();
    //CalculateKeyboard();
    if (CurrentScreenMode == smDetails)
      ReloadAfterDetails(hWnd);
    else
      pListData->Redraw();
  }
  // Notify of item update
  if (pListDataCallLog)
  {
    pListDataCallLog->OutlookContactChanged(oid);
    if (nCurrentTab == tabCallLog &&
      CurrentScreenMode == smDetails &&
      pListDataCallLog->GetItemIdxByOid(oid) == pListData->GetSelection())
    {
      PopulateDetailsInfo(hWnd, pListData->GetSelection());
      ToggleMenu(hWnd, mtCallDetails);
      pListData->Redraw();
    }
  }

  return 0;
}

LRESULT DoSimContactRefresh(HWND hWnd, UINT wMsg, WPARAM wParam, LPARAM lParam)
{
#ifdef LOG_ENABLED
  Logger::Log(TEXT("DoSimContactRefresh"));
#endif

  if (pListDataContacts)
    ReloadList(hWnd, tabContacts);

  if (nCurrentTab != tabCallLog)
  {
    pListData->CalculateHeights();
    //CalculateKeyboard();
    if (CurrentScreenMode == smDetails)
      ExpandDetails(hWnd);
    else
      pListData->Redraw();
  }

  return 0;
}

LRESULT DoPoomPictureLoaded(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  CEOID oid = (CEOID)wParam;
  if (oid == 0)
  { // multiple items loaded, redraw all
    pListData->Redraw();
  }
  else
  {
    int index = pListData->GetItemIdxByOid(oid);
    if (index >= 0)
    {
      if (pListData->IsItemVisible(index))
      {
        pListData->Redraw();
      }
    }
  }

  return 0;
}

LRESULT DoInfoGetCurrentTab(HWND, UINT, WPARAM, LPARAM)
{
  return (LRESULT)nCurrentTab;
}

LRESULT DoInfoGetCallType(HWND, UINT, WPARAM, LPARAM)
{
  return (LRESULT)CallLog_ShownType;
}

LRESULT DoInfoGetContactsList(HWND, UINT, WPARAM, LPARAM)
{
  return (LRESULT)pListDataContacts;
}

//-----------------------------------------------------------------------------
// Miscellaneous Drawing Functions
//
void DrawTransition(HDC hdc, ETransitionType tt)
{
  switch(tt)
  {
  case ttSlideRight:
  case ttSlideLeft:
    DrawTransitionSlide(hdc, tt);
    break;
  case ttShowPopup:
  case ttHidePopup:
    DrawTransitionPopup(hdc, tt);
    break;
  case ttShowSkk:
  case ttHideSkk:
    DrawTransitionSkk(hdc, tt);
    break;
  }
}

void DrawTransitionSlide(HDC hdc, ETransitionType tt)
{
  RECT rContent = rScreen;
  int rScreenWidth = rScreen.right - rScreen.left;
  int rScreenHeight = rScreen.bottom - rScreen.top;   

  rContent.top = rTitlebar.bottom;
  int rHeight = rContent.bottom - rContent.top;
  int width1 = (int)(rScreenWidth * dTransitionPct);
  if (tt == ttSlideLeft)
    width1 = rScreenWidth - width1;
  int width2 = rScreenWidth - width1;

  BitBlt(hdcMem, rContent.left, rContent.top, width1, rHeight, 
    hdcPage1, width2, rContent.top, SRCCOPY);

  BitBlt(hdcMem, width1, rContent.top, width2, rHeight,
    hdcPage2, rContent.left, rContent.top, SRCCOPY);
}

void DrawTransitionPopup(HDC hdc, ETransitionType tt)
{
  int rScreenWidth = rScreen.right - rScreen.left;
  int rScreenHeight = rScreen.bottom - rScreen.top;   

  double cubic = pow(dTransitionPct - 1, 3) + 1;
  if (tt == ttHidePopup)
    cubic = 1 - cubic;

  int nPopWidth = (int)((rPopup.right-rPopup.left) * cubic);
  int nPopHeight = (int)((rPopup.bottom-rPopup.top) * cubic);
  int nPopX = PopupStartX - (PopupStartX * nPopWidth) / rScreenWidth;
  int nPopY = PopupStartY - (int)((PopupStartY - rPopup.top) * cubic);

  BitBlt(hdcMem, rScreen.left, rScreen.top, 
    rScreenWidth, rScreenHeight, 
    hdcPage1, rScreen.left, rScreen.top, SRCCOPY);

  // for faster rendering, don't stretch or alpha-ize the keyboard
  if (Settings::doFastGraphics || !Settings::popupAlpha || Settings::optimizedAlpha)
  {
    BitBlt(hdcMem, nPopX, nPopY, nPopWidth, nPopHeight,
      hdcPage2, rPopup.left, rPopup.top, SRCCOPY);
  }
  else
  {
    GraphicFunctions::BltAlpha(hdcMem,
      nPopX, nPopY, nPopWidth, nPopHeight,
      hdcPage2, rPopup.left, rPopup.top, rPopup.right-rPopup.left, rPopup.bottom-rPopup.top, 220);
  }
}

void DrawTransitionSkk(HDC hdc, ETransitionType tt)
{
  int rScreenWidth = rScreen.right - rScreen.left;
  int rScreenHeight = rScreen.bottom - rScreen.top;   

  double cubic = pow(dTransitionPct - 1, 3) + 1;
  if (tt == ttHideSkk)
    cubic = 1 - cubic;

  int nPopWidth = (int)(pSearchKbd->GetWidth() * cubic);
  int nPopHeight = (int)(pSearchKbd->GetHeight() * cubic);
  int nPopX = rScreen.right - nPopWidth;
  int nPopY = rScreen.bottom - nPopHeight;

  BitBlt(hdcMem, rScreen.left, rScreen.top, 
    rScreenWidth, rScreenHeight - nPopHeight,
    hdcPage1, rScreen.left, rScreen.top, SRCCOPY);

  if (!pSearchKbd->IsTransparent() || Settings::doFastGraphics || Settings::optimizedAlpha)
  {
    BitBlt(hdcMem, nPopX, nPopY, nPopWidth, nPopHeight,
      hdcPage2, pSearchKbd->GetRect().left, pSearchKbd->GetRect().top, SRCCOPY);
  }
  else
  {
    GraphicFunctions::BltAlpha(hdcMem,
      nPopX, nPopY, nPopWidth, nPopHeight,
      hdcPage2, pSearchKbd->GetRect().left, pSearchKbd->GetRect().top, pSearchKbd->GetWidth(), pSearchKbd->GetHeight(), 220);
  }
}

void DrawScreenOn(HDC hdc, EScreenMode screenMode, HDC hdcTmp, const RECT& rClip)
{
  // MAIN CONTENT
  switch (screenMode)
  {
  case smList:
    if (rClip.bottom <= pListData->GetRect().top || rClip.top >= pListData->GetRect().bottom)
      break;

    pListData->Draw(hdc, hdcTmp, rClip);
    pListData->DrawOverlays(hdc, hdcTmp, rClip);
    break;

  case smDetails:
    if (rClip.bottom <= pItemDetails->GetRect().top || rClip.top >= pItemDetails->GetRect().bottom)
      break;
    pItemDetails->Draw(hdc, hdcTmp, rClip);
    pItemDetails->DrawOverlays(hdc, hdcTmp, rClip);
    break;
  }

  // TITLE BAR
  if (rClip.top <= rTitlebar.bottom && !Settings::showSystemTaskbar)
  {
    Titlebar::Draw(hdc, rTitlebar);
  }

  // MENU BAR
  if (rClip.bottom >= rMenubar.top && pBottomMenu != NULL)
  {
    pBottomMenu->Draw(hdc);
  }
}

void DrawKeyboardOn(HDC hdc, const RECT& rKeyboard)
{
  SelectObject(hdc, Theme::KeyboardFont);
  SelectObject(hdc, Theme::hpenPopupGrid);
  SetTextColor(hdc, Theme::rgbPopupText);
  SetBkMode(hdc, TRANSPARENT);
  int x, y, g, h;
  int precalc;

  FillRect(hdc, &rKeyboard, Theme::hbrPopupBackground);

  SetTextAlign(hdc, TA_CENTER);

  // Draw the horizontal lines
  precalc = (rKeyboard.bottom - rKeyboard.top) / nKeyboardRows; // key height
  for (h = 0; h < nKeyboardRows; h++) {
    y = rKeyboard.top + h * precalc;
    MoveToEx(hdc, rKeyboard.left, y, (LPPOINT) NULL);
    LineTo(hdc, rKeyboard.right, y);
  }
  MoveToEx(hdc, rKeyboard.left, rKeyboard.bottom - 1, (LPPOINT) NULL);
  LineTo(hdc, rKeyboard.right, rKeyboard.bottom - 1);

  // Draw the vertical lines
  precalc = (rKeyboard.right - rKeyboard.left) / nKeyboardCols; // key width
  for (g = 0; g < nKeyboardCols; g++) {
    x = rKeyboard.left + g * precalc;
    MoveToEx(hdc, x, rKeyboard.top, (LPPOINT) NULL);
    LineTo(hdc, x, rKeyboard.bottom);
  }
  MoveToEx(hdc, rKeyboard.right - 1, rKeyboard.top, (LPPOINT) NULL);
  LineTo(hdc, rKeyboard.right - 1, rKeyboard.bottom);

  // Draw the letters
  int i = 0;
  TCHAR wcGroup[2] = { 0 };
  precalc = ((GroupHeight - Theme::KeyboardFontSize) / 2); // half cell height
  for (h = 0; h <= nKeyboardRows; h++) {
    y = rKeyboard.top + precalc + (GroupHeight * h);

    for (g = 0; g < nKeyboardCols; g++) {
      x = rKeyboard.left + (GroupWidth / 2) + (GroupWidth * g);

      if (i < pListData->GetGroupPos().Count()) 
      {
        wcGroup[0] = pListData->GetGroupPos().GetGrpByIndex(i++);
        ExtTextOut(hdc, x, y, NULL, NULL, wcGroup, 1, 0);
      }
    }
  }
}

//-----------------------------------------------------------------------------
// Utility functions
//

void InitSurface(HWND hWnd)
{
  HRESULT hr = S_OK;
  HDC hdc;
  hdc = GetDC(hWnd);

  // Update the RECTs for the individual sections
  GetClientRect(hWnd, &rScreen);
  nScreenHeight = rScreen.bottom - rScreen.top;
  int nScreenWidth = rScreen.right - rScreen.left;

  // Initialize the DCs and Bitmaps
  if (hdcMem)
    DeleteDC(hdcMem);
  hdcMem = CreateCompatibleDC(hdc);
  if (hbmMem)
    DeleteObject(hbmMem);
  hbmMem = CreateCompatibleBitmap(hdc, nScreenWidth, nScreenHeight);
  SelectObject(hdcMem, hbmMem);

  if (hdcTmp)
    DeleteDC(hdcTmp);
  hdcTmp = CreateCompatibleDC(hdc);
  if (hbmTmp)
    DeleteObject(hbmTmp);
  hbmTmp = CreateCompatibleBitmap(hdc, nScreenWidth, nScreenHeight);
  SelectObject(hdcTmp, hbmTmp);

  if (hdcPage1)
    DeleteDC(hdcPage1);
  hdcPage1 = CreateCompatibleDC(hdc);
  if (hbmPage1)
    DeleteObject(hbmPage1);
  hbmPage1 = CreateCompatibleBitmap(hdc, nScreenWidth, nScreenHeight);
  SelectObject(hdcPage1, hbmPage1);

  if (hdcPage2)
    DeleteDC(hdcPage2);
  hdcPage2 = CreateCompatibleDC(hdc);
  if (hbmPage2)
    DeleteObject(hbmPage2);
  hbmPage2 = CreateCompatibleBitmap(hdc, nScreenWidth, nScreenHeight);
  SelectObject(hdcPage2, hbmPage2);

  ReleaseDC(hWnd, hdc);

  CalculateRects();
}

void CalculateRects()
{
  // Title bar, with date, carrier, battery, signal strength, etc.
  rTitlebar = rScreen;
  if (Settings::showSystemTaskbar)
    // collapse new titlebar so it is not active
    rTitlebar.bottom = rTitlebar.top;
  else
    // show new titlebar
    rTitlebar.bottom = rTitlebar.top + Theme::TitlebarHeight;

  rMenubar = rScreen;
  if (pBottomMenu)
  {
    rMenubar.top = rMenubar.bottom - Theme::iMenuButtonHeight;
    pBottomMenu->SetRect(rMenubar);
  }
  else
    rMenubar.top = rMenubar.bottom;

  RECT rList = rScreen;
  rList.top = rTitlebar.bottom;
  rList.bottom = rMenubar.top;

  if (pItemDetails)
    pItemDetails->SetRect(rList);

  if (pSearchKbd)
  {
    RECT rcSkk = rScreen;
    rcSkk.top = rScreen.bottom - Theme::iSearchKeyboardHeight;
    pSearchKbd->SetRect(rcSkk);
    if (SearchKbdShown)
    {
      if (!pSearchKbd->IsTransparent())
        rList.bottom = pSearchKbd->GetRect().top;
    }
  }

  if (pSearchBox && pSearchBox->IsVisible())
  {
    RECT rc = rList;
    rc.top = rList.bottom - Theme::SecondaryListFontSize * 3 / 2;
    pSearchBox->SetRect(rc);
    rList.bottom = rc.top;
  }

  if (pListData)
    pListData->SetRect(rList);
}

void CalculateKeyboard()
{
  nKeyboardRows = 1;
  nKeyboardCols = 1;
  GroupWidth = 1;
  GroupHeight = 1;

  if (!pListData)
    return;

  // Calculate how many rows / cols for the keyboard
  int nScreenWidth = rScreen.right - rScreen.left;

  if (nScreenWidth <= 0 || nScreenHeight <= 0)
    return;

  double screenRatio = (double)nScreenWidth / nScreenHeight;
  while (nKeyboardRows * nKeyboardCols < pListData->GetGroupPos().Count())
  {
    if (nScreenWidth < nScreenHeight)
    {
      nKeyboardRows = (int)(++nKeyboardCols / screenRatio);
    }
    else
    {
      nKeyboardCols = (int)(++nKeyboardRows * screenRatio);
    }
  }
  if (nScreenWidth < nScreenHeight)
  {
    while ((nKeyboardRows - 1) * nKeyboardCols >= pListData->GetGroupPos().Count())
    {
      nKeyboardRows--;
    }
  }
  else
  {
    while (nKeyboardRows * (nKeyboardCols - 1) >= pListData->GetGroupPos().Count())
    {
      nKeyboardCols--;
    }
  }
  if (nKeyboardCols <= 0) nKeyboardCols = 1;
  if (nKeyboardRows <= 0) nKeyboardRows = 1;
  GroupWidth = nScreenWidth / nKeyboardCols;
  GroupHeight = nScreenHeight / nKeyboardRows;
}

void SetMenu(HWND hWnd, Menu* pMenu, bool redraw)
{
  if (pBottomMenu != pMenu)
  {
    pBottomMenu = pMenu;
    CalculateRects();
    if (redraw)
      ::InvalidateRect(hWnd, &rMenubar, FALSE);
  }
}

void StartTransition(HWND hWnd, ETransitionType tr, int duration)
{
  if (duration == 0)
  {
    trTransitionType = tr;
    TransitionFinished(hWnd);
    InvalidateRect(hWnd, &rScreen, FALSE);
  }
  else
  {
    bTransitioning = true;
    nTransitionDuration = duration;
    dTransitionPct = 0.0;
    trTransitionType = tr;
    dwTransitionStart = ::GetTickCount();

    if (tr == ttSlideLeft)
    {
      ::BitBlt(hdcPage1, rScreen.left, rScreen.top, rScreen.right-rScreen.left, rScreen.bottom-rScreen.top,
        hdcMem, rScreen.left, rScreen.top, SRCCOPY);
      DrawScreenOn(hdcPage2, smDetails, hdcTmp, rScreen);
    }
    else if (tr == ttSlideRight)
    {
      DrawScreenOn(hdcPage1, smList, hdcTmp, rScreen);
      ::BitBlt(hdcPage2, rScreen.left, rScreen.top, rScreen.right-rScreen.left, rScreen.bottom-rScreen.top,
        hdcMem, rScreen.left, rScreen.top, SRCCOPY);
    }
    else if (tr == ttShowPopup || tr == ttHidePopup)
    {
      if (rPopup.bottom - rPopup.top < rScreen.bottom - rScreen.top)
      {
        ::FillRect(hdcPage1, &rScreen, Theme::hbrListBackground);
        GraphicFunctions::BltAlpha(hdcPage1, rScreen.left, rScreen.top, rScreen.right-rScreen.left, rScreen.bottom-rScreen.top,
          hdcMem, 120);
      }
      else
      {
        ::BitBlt(hdcPage1, rScreen.left, rScreen.top, rScreen.right-rScreen.left, rScreen.bottom-rScreen.top,
          hdcMem, rScreen.left, rScreen.top, SRCCOPY);
      }
      switch(PopupWindow)
      {
      case pwKeyboard:
        DrawKeyboardOn(hdcPage2, rScreen);
        break;
      case pwCategories:
        if (pListCategories)
          pListCategories->Draw(hdcPage2, hdcTmp, rScreen);
        break;
      }
    }
    else if (tr == ttShowSkk || tr == ttHideSkk)
    {
      if (!pSearchKbd)
      {
        bTransitioning = false;
        return;
      }
      ::BitBlt(hdcPage1, rScreen.left, rScreen.top, rScreen.right-rScreen.left, rScreen.bottom-rScreen.top,
        hdcMem, rScreen.left, rScreen.top, SRCCOPY);
      pSearchKbd->Draw(hdcPage2, rScreen);
    }

    SetTimer(hWnd, IDT_TIMER_TRANSITION, TRANSITION_REFRESH_RATE, NULL);
  }
}

void TransitionFinished(HWND hWnd)
{
  bTransitioning = false;

  switch (trTransitionType)
  {
  case ttSlideRight:
    if (pListData->IsFiltered())
      ToggleSearchKeyboard(hWnd, true);

    CurrentScreenMode = smList;
    pItemDetails->Clear();
    pListData->CalculateHeights();
    //CalculateKeyboard();
    break;
  case ttSlideLeft:
    if (pListData->IsFiltered())
      ToggleSearchKeyboard(hWnd, false);

    CurrentScreenMode = smDetails;
    break;
  case ttShowPopup:
    PopupShown = true;
    break;
  case ttHidePopup:
    switch(PopupWindow)
    {
    case pwCategories:
      if (pListCategories)
      {
        delete pListCategories;
        pListCategories = NULL;
      }
      break;
    case pwKeyboard:
      pListData->ScrollToSelection();
      break;
    }
    PopupShown = false;
    PopupWindow = pwNone;
    break;
  case ttShowSkk:
    ToggleSearchKeyboard(hWnd, true);
    break;
  case ttHideSkk:
    ToggleSearchKeyboard(hWnd, false);
    break;
  }
}

void ReloadAfterDetails(HWND hWnd)
{
  if (contactDetailsInHistoryIndex >= 0)
  {
    contactDetailsInHistoryIndex = -1;
    nCurrentTab = tabCallLog;
    pListData = pListDataCallLog;
  }
  
  pItemDetails->Clear();
  ToggleMenu(hWnd, mtList);
  StartTransition(hWnd, CurrentScreenMode == smList ? ttSlideLeft : ttSlideRight, 0);
}

void ExpandDetails(HWND hWnd)
{
  //if (CurrentScreenMode == smList)
  //  ToggleMenu(hWnd, nCurrentTab == tabCallLog ? mtCallDetails : mtDetails);
  //else
  //  ToggleMenu(hWnd, mtList);

  //StartTransition(hWnd, CurrentScreenMode == smList ? ttSlideLeft : ttSlideRight, Settings::detailsSlideTime);

  if (CurrentScreenMode == smList)
  {
    ToggleMenu(hWnd, nCurrentTab == tabCallLog ? mtCallDetails : mtDetails);
  }
  else
  {
    if (Settings::callLogDetailsBackToCallLog && contactDetailsInHistoryIndex >= 0)
    {
      nCurrentTab = tabCallLog;
      pListData = pListDataCallLog;
      pListData->SetSelection(contactDetailsInHistoryIndex, false);
      pListData->CalculateHeights();
      CalculateRects();
      PopulateDetailsInfo(hWnd, contactDetailsInHistoryIndex);
      pListData->Redraw();
      ToggleMenu(hWnd, mtCallDetails);
      contactDetailsInHistoryIndex = -1;
      return;
    }
    else
    {
      ToggleMenu(hWnd, mtList);
    }
  }

  StartTransition(hWnd, CurrentScreenMode == smList ? ttSlideLeft : ttSlideRight, Settings::detailsSlideTime);
}

bool PopulateDetailsInfo(HWND hWnd, int item)
{
  if (item < 0 || item >= pListData->GetCount())
    return false;

  if (!pListData->GetItem(item)->GetDetails(hWnd, *pItemDetails))
    return false;

  CalculateRects();
  pItemDetails->Prepare(hdcTmp, pItemDetails->GetRect());
  return true;
}

void SwitchTab(HWND hWnd, EMainTabs which)
{
  try
  {
#ifdef LOG_ENABLED
    LOGTIMER_START
#endif

      if (pListData)
        pListData->StopScrolling();

    switch (which)
    {
    case tabFavorites:
      if (!Settings::enableListCaching || pListDataFavorites == NULL)
        ReloadList(hWnd, tabFavorites);
      pListData = pListDataFavorites;
      break;
    case tabCallLog:
      if (!Settings::enableListCaching || pListDataCallLog == NULL)
        ReloadList(hWnd, tabCallLog);
      pListData = pListDataCallLog;
      // reset missed calls here
      if (CallLog_ShownType == ctAll || CallLog_ShownType == ctMissed)
        PhoneUtils::ResetMissedCalls();
      break;
    case tabContacts:
    default:
      if (!Settings::enableListCaching || pListDataContacts == NULL)
        ReloadList(hWnd, tabContacts);
      pListData = pListDataContacts;
      break;
    }
#ifdef LOG_ENABLED
    LOGTIMER_LOG("SwitchTab")
#endif

      CurrentScreenMode = smList;
    nCurrentTab = which;

    pListData->CalculateHeights();
    //CalculateKeyboard();
    ToggleMenu(hWnd, mtList);
    CalculateRects();
  }
  catch(const TCHAR* text)
  {
    MessageBox(hWnd, text, TEXT("Error"), MB_OK | MB_ICONEXCLAMATION);
  }
  catch(...)
  {
    MessageBox(hWnd, TEXT("Unknown error"), TEXT("Error"), MB_OK | MB_ICONEXCLAMATION);
  }
}

void ReloadList(HWND hWnd, EMainTabs forTab, bool fullReload)
{
  if (forTab == nCurrentTab)
  {
    ClearFilter(hWnd, false);
  }

  switch(forTab)
  {
  case tabFavorites:
    if (fullReload && pListDataFavorites)
    {
      delete pListDataFavorites;
      pListDataFavorites = NULL;
    }
    PoomData::SubscribeToNotifications(olFolderContacts, PIMFOLDERNOTIFICATION_REMOTE);
    if (!pListDataFavorites)
    {
      pListDataFavorites = new ListDataPoom(hWnd, Settings::favorite_category);
      if (!pListDataFavorites)
        throw TEXT("Not enough memory to load Favorites!");
    }
    else
      pListDataFavorites->Reload(pListDataFavorites->GetCategory());

    PoomData::SubscribeToNotifications(olFolderContacts, PIMFOLDERNOTIFICATION_ALL);
    break;

  case tabCallLog:
    if (!pListDataCallLog)
    {
      pListDataCallLog = new ListDataCallLog(hWnd, CallLog_ShownType);
      if (!pListDataCallLog)
        throw TEXT("Not enough memory to load Call History!");
    }
    else
    {
      pListDataCallLog->Reload(CallLog_ShownType, fullReload);
    }
    break;

  case tabContacts:
  default:
    if (fullReload && pListDataContacts)
    {
      delete pListDataContacts;
      pListDataContacts = NULL;
    }

    PoomData::SubscribeToNotifications(olFolderContacts, PIMFOLDERNOTIFICATION_REMOTE);
    if (!pListDataContacts)
    {
      pListDataContacts = new ListDataPoom(hWnd);
      if (!pListDataContacts)
        throw TEXT("Not enough memory to load Contacts!");
    }
    else
      pListDataContacts->Reload();
    PoomData::SubscribeToNotifications(olFolderContacts, PIMFOLDERNOTIFICATION_ALL);

    if (Settings::showSimContacts)
    {
      pListDataContacts->PopulateSim();
    }

    break;
  }
}

//burt: mouse gestures begin
bool DoMouseGesture(HWND hWnd, int dx, int dy)
{
  EMouseGesture gesture = ParseMouseGesture(MouseMove_dx, MouseMove_dy);

  if (PopupShown)
  {
    switch (PopupWindow)
    {
    case pwKeyboard:
    case pwCategories:
      switch(gesture)
      {
        // Right to Left in Keyboard hides it
      case mgRtoL:
        StartTransition(hWnd, ttHidePopup, Settings::popupHideTime);
        return true;
        // None
      case mgNone:
      default:
        break;
      }
      break;
    }
  }
  else
  {
    switch (CurrentScreenMode)
    {
      // List Mode
    case smList:
      // Call History
      if (nCurrentTab == tabCallLog) {
        switch(gesture) {
    case mgRtoL:
      RollCallHistory(hWnd, false);
      return true;
    case mgLtoR:
      RollCallHistory(hWnd, true);
      return true;
      // None
    case mgNone:
    default:
      break;
        }
      }
      break;

      // Details Mode
    case smDetails:
      switch(gesture) {
        // Right to Left in Details is same as Back
        // Left to Right too
    case mgRtoL:
    case mgLtoR:
      PostCommand(hWnd, CMD_DETAILS_BACK);
      return true;
      // None
    case mgNone:
    default:
      break;
      }
      break;
    }
  }

  return false;
}

EMouseGesture ParseMouseGesture(int dx, int dy)
{
  if (dx == 0 && dy == 0)
    return mgNone;

  int deltaAbsX = (dx * 100) / (rScreen.right - rScreen.left);
  int deltaAbsY = (dy * 100) / (nScreenHeight);

  if (abs(deltaAbsX) < MOUSE_GESTURE_MINDELTAX)
    deltaAbsX = 0;

  if (abs(deltaAbsY) < MOUSE_GESTURE_MINDELTAY)
    deltaAbsY = 0;


  // none
  if (deltaAbsX == 0 && deltaAbsY == 0)
    return mgNone;
  // left to right
  if (deltaAbsX > 0 && deltaAbsY == 0)
    return mgLtoR;
  // right to left
  else if (deltaAbsX < 0 && deltaAbsY == 0)
    return mgRtoL;
  // up to down
  else if (deltaAbsX == 0 && deltaAbsY > 0)
    return mgUtoD;
  // down to up
  else if (deltaAbsX == 0 && deltaAbsY < 0)
    return mgDtoU;
  // up-right to down-left
  else if (deltaAbsX < 0 && deltaAbsY > 0)
    return mgDRtoUL;
  // down-left to up-right
  else if (deltaAbsX > 0 && deltaAbsY < 0)
    return mgDLtoUR;
  // up-left to down-right
  else if (deltaAbsX > 0 && deltaAbsY > 0)
    return mgULtoDR;
  // down-right to up-left
  else if (deltaAbsX < 0 && deltaAbsY < 0)
    return mgDRtoUL;

  return mgNone;
}
//burt: mouse gestures end

void RollCallHistory(HWND hWnd, bool forward)
{
  if (forward)
  {
    // rotate displayed call type forward
    if (CallLog_ShownType + 1 == ctCount)
      CallLog_ShownType = (CallType)0;
    else
      CallLog_ShownType = CallType(CallLog_ShownType + 1);
  }
  else
  {
    // rotate displayed call type backward
    if (CallLog_ShownType == 0)
      CallLog_ShownType = (CallType)(ctCount - 1);
    else
      CallLog_ShownType = CallType(CallLog_ShownType - 1);
  }
  if (nCurrentTab == tabCallLog)
  {
    pListData->StopScrolling();
    ReloadList(hWnd, tabCallLog);
    // reset missed calls here
    if (CallLog_ShownType == ctAll || CallLog_ShownType == ctMissed)
      PhoneUtils::ResetMissedCalls();
    pListData->CalculateHeights();
    //CalculateKeyboard();
    CalculateRects();
  }
  else
  {
    ReloadList(hWnd, tabCallLog);
    SwitchTab(hWnd, tabCallLog);
  }
  pListMenu->SetCallType(CallLog_ShownType);
  pListData->Redraw();
  InvalidateRect(hWnd, &rMenubar, FALSE);
}

// active tab clicked again
bool DoTabAction(HWND hWnd)
{
  switch (nCurrentTab)
  {
  case tabFavorites:
    if (pListCategories)
    {
      delete pListCategories;
    }
    pListCategories = new CListCategories(hWnd);
    if (pListCategories)
    {
      CopyRect(&rPopup, &rScreen);
      if (pListCategories->GetHeight() < rScreen.bottom - rScreen.top)
      {
        rPopup.top = (rPopup.top + rPopup.bottom - pListCategories->GetHeight()) / 2;
        rPopup.bottom = rPopup.top + pListCategories->GetHeight();
      }
      pListCategories->SetRect(rPopup);
      PopupWindow = pwCategories;
      PopupStartX = 0;
      PopupStartY = nScreenHeight;
      StartTransition(hWnd, ttShowPopup, Settings::popupShowTime);
    }
    break;

  case tabCallLog:
    if (CurrentScreenMode == smList)
    {
      RollCallHistory(hWnd, true);
      return true;
    }
    break;

  case tabContacts:
    break;
  }

  return false;
}

bool DoDefaultAction(HWND hWnd, int item)
{
  if (item != -1)
  {
    return pListData->GetAt(item)->OnDefaultAction(hWnd);
  }
  return false;
}

void DoPopupLButtonDown(HWND hWnd, int x, int y)
{
  switch(PopupWindow)
  {
  case pwKeyboard:
    break;

  case pwCategories:
    if (pListCategories)
      pListCategories->OnMouseDown(x, y);
    break;
  }
}

void DoPopupMouseMove(HWND hWnd, int x, int y)
{
  switch(PopupWindow)
  {
  case pwKeyboard:
    break;

  case pwCategories:
    if (pListCategories)
      pListCategories->OnMouseMove(x, y);    
    break;
  }
}

void DoPopupLButtonUp(HWND hWnd, int x, int y)
{
  switch(PopupWindow)
  {
  case pwKeyboard:
    {
      int index = (y / GroupHeight) * (rScreen.right - rScreen.left) / GroupWidth 
        + (x - rScreen.left) / GroupWidth;

      pListData->SelectItemByGroup(index);

      StartTransition(hWnd, ttHidePopup, Settings::popupHideTime);
    }
    break;

  case pwCategories:
    if (pListCategories)
      pListCategories->OnMouseUp(x, y);
    break;
  }
}

void DoTitlebarClick(HWND hWnd, int x, int y)
{
  if (Settings::doExitOnMinimize)
    DestroyWindow(hWnd);
  else
    ShowWindow(hWnd, SW_MINIMIZE);
}

void OnSearchBoxChange(HWND hWnd)
{
  TCHAR txtSearch[SKK_MAX_STRING_LENGTH] = {0};
  pSearchBox->GetText(txtSearch, SKK_MAX_STRING_LENGTH);

  // TODO: See about applying this filter on a thread, so that the UI is usable while the filtering is happening with many contacts
  pListData->ApplyFilter(txtSearch);
}

void OnCmdListFavorites(HWND hWnd, int nCmdFlags)
{
  if (nCmdFlags & CMDF_MENU_LONGTAP)
  {
    if (nCurrentTab != tabFavorites)
    {
      SwitchTab(hWnd, tabFavorites);
      pListMenu->SetSelected(nCurrentTab);
      InvalidateRect(hWnd, &rMenubar, FALSE);
    }
    DoTabAction(hWnd);
  }
  else
  {
    if (nCurrentTab == tabFavorites)
    {
      DoTabAction(hWnd);
    }
    else
    {
      SwitchTab(hWnd, tabFavorites);
      pListData->Redraw();
    }
  }
}

void OnCmdListCallLog(HWND hWnd, int nCmdFlags)
{
  if (nCurrentTab == tabCallLog)
  {
    DoTabAction(hWnd);
  }
  else
  {
    SwitchTab(hWnd, tabCallLog);
    pListData->Redraw();
  }
}

void OnCmdListContacts(HWND hWnd, int nCmdFlags)
{
  if (Settings::OpenSettingsOnLongTap && (nCmdFlags & CMDF_MENU_LONGTAP))
  {
    wchar_t path[MAX_PATH+1];
    if (GetConfigExe(path))
    {
      PROCESS_INFORMATION pi;

      if (!CreateProcess(path, 0, 0, 0, 0, CREATE_NEW_CONSOLE, 0, 0, 0, &pi))
      {
        DWORD err = GetLastError();

        switch (err)
        {
        case ERROR_FILE_NOT_FOUND:
          MessageBox(0,L"Could not launch iContact Config: ERROR_FILE_NOT_FOUND",L"",0);
          break;
        case ERROR_PATH_NOT_FOUND:
          MessageBox(0,L"Could not launch iContact Config: ERROR_PATH_NOT_FOUND",L"",0);
          break;
        case ERROR_DDE_FAIL:
          MessageBox(0,L"Could not launch iContact Config: ERROR_DDE_FAIL",L"",0);
          break;
        case ERROR_NO_ASSOCIATION:
          MessageBox(0,L"Could not launch iContact Config: ERROR_NO_ASSOCIATION",L"",0);
          break;
        case ERROR_ACCESS_DENIED:
          MessageBox(0,L"Could not launch iContact Config: ERROR_ACCESS_DENIED",L"",0);
          break;
        case ERROR_DLL_NOT_FOUND:
          MessageBox(0,L"Could not launch iContact Config: ERROR_DLL_NOT_FOUND",L"",0);
          break;
        case ERROR_CANCELLED:
          MessageBox(0,L"Could not launch iContact Config: ERROR_CANCELLED",L"",0);
          break;
        case ERROR_NOT_ENOUGH_MEMORY:
          MessageBox(0,L"Could not launch iContact Config: ERROR_NOT_ENOUGH_MEMORY",L"",0);
          break;
        case ERROR_SHARING_VIOLATION:
          MessageBox(0,L"Could not launch iContact Config: ERROR_SHARING_VIOLATION",L"",0);
          break;
        default:
          MessageBox(0,L"Could not launch iContact Config: UNKNOWN ERROR",L"",0);
          break;
        }
      }
    }
  }
  else
  {
    if (nCurrentTab == tabContacts)
    {
      DoTabAction(hWnd);
    }
    else
    {
      SwitchTab(hWnd, tabContacts);
      pListData->Redraw();
    }
  }
}

void OnCmdListDialer(HWND hWnd, int nCmdFlags)
{
  bool defaultOnly = (nCmdFlags & CMDF_MENU_LONGTAP) != 0;
  PhoneUtils::RunDialer(defaultOnly);

  if (Settings::doMinimizeOnAction)
  {
    SetWindowPos(hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
  }
  if (Settings::doExitOnAction)
  {
    DestroyWindow(hWnd);
  }
}

void OnCmdListKeyboard(HWND hWnd, int nCmdFlags)
{
  ClearFilter(hWnd, false);
  if (nCmdFlags & CMDF_MENU_LONGTAP)
  {
    if (Settings::t9SearchByDefault && nCurrentTab != tabCallLog && pListData->IsShowGroupHeaders())
    { // Jump-To
      PopupWindow = pwKeyboard;
      PopupStartX = rScreen.right - rScreen.left;
      PopupStartY = nScreenHeight;
      ::CopyRect(&rPopup, &rScreen);
      CalculateKeyboard();
      StartTransition(hWnd, ttShowPopup, Settings::popupShowTime);        
    }
    else
    { // T9 Search
      StartTransition(hWnd, ttShowSkk, Settings::popupShowTime);
      if (pSearchKbd->IsTransparent())
        pListData->SetExtraHeight(pSearchKbd->GetHeight());
      pListData->CalculateHeights();
      //CalculateKeyboard();
    }
  }
  else
  {
    if (Settings::t9SearchByDefault || nCurrentTab == tabCallLog || !pListData->IsShowGroupHeaders())
    {
      StartTransition(hWnd, ttShowSkk, Settings::popupShowTime);
      if (pSearchKbd->IsTransparent())
        pListData->SetExtraHeight(pSearchKbd->GetHeight());
      pListData->CalculateHeights();
      //CalculateKeyboard();
    }
    else
    {
      PopupWindow = pwKeyboard;
      PopupStartX = rScreen.right - rScreen.left;
      PopupStartY = nScreenHeight;
      ::CopyRect(&rPopup, &rScreen);
      CalculateKeyboard();
      StartTransition(hWnd, ttShowPopup, Settings::popupShowTime);
    }
  }
}

void OnCmdDetailsBack(HWND hWnd, int nCmdFlags)
{
  ExpandDetails(hWnd);
}

void OnCmdDetailsView(HWND hWnd, int nCmdFlags)
{
  if (pListData->GetSelection() != -1)
    pListData->GetSelectedItem()->OnCommand(hWnd, CMD_DETAILS_VIEW);
}

void OnCmdDetailsEdit(HWND hWnd, int nCmdFlags)
{
  if (pListData->GetSelection() != -1)
    pListData->GetSelectedItem()->OnCommand(hWnd, CMD_DETAILS_EDIT);
}

void OnCmdDetailsDelete(HWND hWnd, int nCmdFlags)
{
  if (pListData->GetSelection() != -1)
  {
    int res = ::MessageBox(hWnd, Language::detailsdelwarning, Language::detailsdelwarningt, MB_YESNO | MB_ICONEXCLAMATION);
    if (res == IDYES)
    {
      ExpandDetails(hWnd);    
      pListData->GetSelectedItem()->OnCommand(hWnd, CMD_DETAILS_DELETE);
    }
  }
}

void OnCmdDetailsFavorite(HWND hWnd, int nCmdFlags)
{
  if (pListData->GetSelection() != -1)
  {
    if (nCurrentTab == tabFavorites && pListDataFavorites->IsFavoritesCat())
    {
      int res = ::MessageBox(hWnd, Language::favremovewarning, Language::favremovewarningt, MB_YESNO | MB_ICONEXCLAMATION);
      if (res == IDYES)
      {
        ExpandDetails(hWnd);
        ToggleFavorite(hWnd, pListData->GetSelection());
        return;
      }
    }
    else
    {
      ToggleFavorite(hWnd, pListData->GetSelection());
      pItemDetails->SetSelection();
      pItemDetails->Redraw();
    }
  }
}

// call log
void OnCmdDetailsClNewContact(HWND hWnd, int nCmdFlags)
{
  if (pListData->GetSelection() != -1)
  {
    if (nCmdFlags & CMDF_MENU_LONGTAP)
    {
      if (pListDataCallLog->GetSelectedItem() >= 0 && pListDataCallLog->GetSelectedItem()->GetDialingNumber() != NULL)
      {
        // Copy to clipboard
        HLOCAL clipbuffer;

        OpenClipboard(hWnd); 
        EmptyClipboard();

        clipbuffer = LocalAlloc(0, (PRIMARY_TEXT_LENGTH + 1) * sizeof(TCHAR));
        _tcsncpy((TCHAR *)clipbuffer, pListDataCallLog->GetSelectedItem()->GetDialingNumber(), PRIMARY_TEXT_LENGTH);
        SetClipboardData(CF_UNICODETEXT, clipbuffer);

        CloseClipboard();

        // Switch to contacts tab
        bIgnoreNextMouseUp = true;
        ExpandDetails(hWnd);
        SwitchTab(hWnd, tabContacts);
      }
    }
    else
    {
      pListData->GetSelectedItem()->OnCommand(hWnd, CMD_DETAILS_CL_NEWCONTACT);
    }
  }
}

void OnCmdDetailsClGoToContact(HWND hWnd, int nCmdFlags)
{
  if (pItemDetails->GetOid() != 0)
  {
    if (pListDataContacts == NULL)
      ReloadList(hWnd, tabContacts);
    if (pListDataContacts)
    {
      int contactIdx = pListDataContacts->GetItemIdxByOid(pItemDetails->GetOid());
      if (contactIdx >= 0)
      {
        contactDetailsInHistoryIndex = pListData->GetSelection();
        nCurrentTab = tabContacts;
        pListData = pListDataContacts;
        pListData->SetSelection(contactIdx, false);
        pListData->CalculateHeights();
        //CalculateKeyboard();
        CalculateRects();
        PopulateDetailsInfo(hWnd, contactIdx);
        pListData->Redraw();
        ToggleMenu(hWnd, mtDetails);
      }
    }
  }
}

void OnCmdDetailsClDeleteCall(HWND hWnd, int nCmdFlags)
{
  if (pListData->GetSelection() != -1)
  {
    int res = ::MessageBox(hWnd, Language::calldelwarning, Language::calldelwarningt, MB_YESNO | MB_ICONEXCLAMATION);
    if (res == IDYES)
    {
      pListData->GetSelectedItem()->OnCommand(hWnd, CMD_DETAILS_CL_DELETECALL);
      // Notification from DB will not be sent,
      // so update the list manually
      ReloadList(hWnd, tabCallLog, true);
      pListData->CalculateHeights();
      //CalculateKeyboard();
      ExpandDetails(hWnd);
      return;
    }
  }
}

// sim
void OnCmdDetailsSimNewContact(HWND hWnd, int nCmdFlags)
{
  if (pListData->GetSelection() != -1)
  {
    if (pListData->GetSelectedItem()->OnCommand(hWnd, CMD_DETAILS_SIM_NEWCONTACT))
      ExpandDetails(hWnd);
  }
}

void OnCmdDetailsSimGoToOutlook(HWND hWnd, int nCmdFlags)
{
  if (pListData->GetSelection() != -1)
    pListData->GetSelectedItem()->OnCommand(hWnd, CMD_DETAILS_SIM_GOTOOUTLOOK);
}

LPTSTR getStringPropertyFromMapi(CEPROPVAL *pValSrc) 
{
  LPTSTR value(TEXT(""));
  if (!(pValSrc->wFlags & CEDB_PROPNOTFOUND)) {
    value = pValSrc->val.lpwstr;
  }
  return value;    
}

bool GetContactRingtone(IItem *pItem, LPTSTR pszRingtone, DWORD dwBufferSize)
{
  HRESULT hr;
  CEPROPID rgPropId = PIMPR_RINGTONE;
  CEPROPVAL * prgPropval = NULL;
  ULONG cbBuffer = 0;
  HANDLE hHeap = GetProcessHeap();
  hr = pItem->GetProps(&rgPropId, CEDB_ALLOWREALLOC, 1, &prgPropval, &cbBuffer, hHeap);
  hr = StringCchCopy(pszRingtone, dwBufferSize, getStringPropertyFromMapi(prgPropval));
  HeapFree(hHeap, 0, prgPropval);
  return SUCCEEDED(hr);
}

bool SetContactRingtone(IItem *pItem, LPTSTR Ringtone)
{
    HRESULT hr;
    CEPROPVAL prgPropval = {0};
    prgPropval.propid = PIMPR_RINGTONE;
    prgPropval.val.lpwstr = Ringtone;
    hr = pItem->SetProps(0, 1, &prgPropval);
    return SUCCEEDED(hr);
}
  
void OnCmdDetailsSetRingtone(HWND hWnd, int nCmdFlags)
{
  if (pListData->GetSelection() < 0)
    return;
  CListItemBase* pListItem = pListData->GetSelectedItem();

  if (!pListItem || pListItem->oId == 0)
    return;

  TCHAR    szFile[MAX_PATH] = {0};

  if (nCmdFlags != 0)
  {
    OPENFILENAMEEX  ofnex = {0};

    ofnex.lStructSize     = sizeof(ofnex);
    ofnex.hwndOwner       = hWnd;
    ofnex.lpstrFile       = szFile;
    ofnex.nMaxFile        = sizeof(szFile)/sizeof(szFile[0]);
    ofnex.ExFlags         = OFN_EXFLAG_DETAILSVIEW;
    ofnex.lpstrTitle      = Language::selectringtone;
    ofnex.lpstrFilter     = TEXT("All media files\0*.mp3;*wma;*.mid\0MP3 (*.mp3)\0*.mp3\0WMA (*.wma)\0*.wma\0MIDI (*.mid)\0*.mid\0");

    if (!GetOpenFileNameEx(&ofnex))
      return;
  }
  IDispatch* pDisp = NULL;
  IItem* pItem = NULL;
  if (FAILED(PoomData::GetApp()->GetItemFromOid(pListItem->oId, &pDisp)))
    return;
  if (FAILED(pDisp->QueryInterface(IID_IItem, (LPVOID*)&pItem)))
  {
    pDisp->Release();
    return;
  }
  if (!SetContactRingtone(pItem, szFile) || !SUCCEEDED(pItem->Save()))
    ::MessageBox(hWnd, Language::ringtonesavefailed, Language::error, MB_OK | MB_ICONEXCLAMATION);
  pItem->Release();
  pDisp->Release();
}

void OnCmdDetailsSetPicture(HWND hWnd, int nCmdFlags)
{
  if (pListData->GetSelection() < 0)
    return;
  CListItemBase* pItem = pListData->GetSelectedItem();

  if (!pItem || pItem->oId == 0)
    return;

  TCHAR    szFile[MAX_PATH];
  OPENFILENAMEEX  ofnex = {0};

  ofnex.lStructSize     = sizeof(ofnex);
  ofnex.hwndOwner       = hWnd;
  ofnex.lpstrFile       = szFile;
  ofnex.nMaxFile        = sizeof(szFile)/sizeof(szFile[0]);
  ofnex.lpstrFilter     = TEXT("JPEG (*.jpg; *.jpeg)\0*.jpg;*.jpeg\0PNG (*.png)\0*.png\0GIF (*.gif)\0*.gif\0Bitmap (*.BMP)\0*.bmp\0");
  ofnex.ExFlags         = OFN_EXFLAG_THUMBNAILVIEW;

  if (!GetOpenFileNameEx(&ofnex))
    return;

  if (!PoomPictures::AssignPicture(pItem->oId, szFile))
  {
    ::MessageBox(hWnd, Language::picsavefailed, Language::error, MB_OK | MB_ICONEXCLAMATION);
  }
}

void OnCmdListShowDetails(HWND hWnd, int nCmdFlags)
{
  if (pListData->GetSelection() != -1 && PopulateDetailsInfo(hWnd, pListData->GetSelection()))
  {
    ExpandDetails(hWnd);
  }
}

void OnCmdListSetCategory(HWND hWnd, int nCmdFlags)
{
  if (pListCategories != NULL && pListDataFavorites != NULL)
  {
    if (pListCategories->GetSelection() != -1)
    {
      const TCHAR* szCategory = pListCategories->GetCategoryName(pListCategories->GetSelection());
      pListDataFavorites->Reload(szCategory);
      pListDataFavorites->CalculateHeights();
      //CalculateKeyboard();
      CalculateRects();

      OnCmdListHideCategories(hWnd, 0);
    }
  }
}

void OnCmdListHideCategories(HWND hWnd, int nCmdFlags)
{
  if (pListCategories)
  {
    StartTransition(hWnd, ttHidePopup, Settings::popupHideTime);
  }
}

void ToggleMenu(HWND hWnd, EMenuType type)
{
  bool  needRedraw = false;
  Menu* pMenu = pBottomMenu;

  switch(type)
  {
  case mtList:
    pMenu = pListMenu;
    if (pMenu->GetSelected(nCurrentTab) == false)
    {
      pMenu->SetSelected(nCurrentTab);
      needRedraw = true;
    }
    break;

  case mtDetails:
    pMenu = pDetailsMenu;
    if (pListData->GetSelection() != -1)
    {
      const CListItemBase* pItem = pListData->GetSelectedItem();
      if (pItem->GetType() == citSim)
        pMenu = pSimDetailsMenu;
      else if (pItem->GetType() == citPoom)
        pMenu->SetItemStateBit(pMenu->GetItemIndex(CMD_DETAILS_FAVORITE), misChecked, ((CListItemPoom*)pItem)->isFavorite);
    }
    break;

  case mtCallDetails:
    pMenu = pCallDetailsMenu;
    break;
  }

  if (pMenu && pMenu != pListMenu)
  {
    if (pListData->GetSelection() != -1)
    {
	  // @IMPROVED 2010-06-14 Nocky
	  int n = pMenu->GetItemCount();
      for (int i = 0; i < n; i++)
      {
        int cmd = pMenu->GetItemCommand(i);
        pMenu->SetItemStateBit(i, misHidden, !pListData->GetSelectedItem()->IsCommandValid((MenuCommands)cmd));
      }
    }
  }

  needRedraw |= pMenu != pBottomMenu;
  SetMenu(hWnd, pMenu, false);
  if (needRedraw)
    InvalidateRect(hWnd, &rMenubar, FALSE);
}

void ToggleFavorite(HWND hWnd, int item)
{
  if (item < 0 || item >= pListData->GetCount())
    return;

  pListData->GetItem(item)->OnCommand(hWnd, CMD_DETAILS_FAVORITE);

  if (pBottomMenu == pDetailsMenu)
  {
    if (pListData->GetItem(item)->GetType() == citPoom)
    {
      CListItemPoom* pItem = (CListItemPoom*)pListData->GetItem(item);
      pDetailsMenu->SetItemStateBit(pDetailsMenu->GetItemIndex(CMD_DETAILS_FAVORITE), misChecked, pItem->isFavorite);
    }
    InvalidateRect(hWnd, &pBottomMenu->GetRect(), FALSE);
  }

  ReloadList(hWnd, tabFavorites);
  if (CurrentScreenMode == smList && nCurrentTab == tabFavorites)
  {
    CalculateRects();
    pListData->CalculateHeights();
    //CalculateKeyboard();
    pListData->Redraw();
  }
}

void ClearFilter(HWND hWnd, bool redraw)
{
  if (!SearchKbdShown && (!pSearchBox || !pSearchBox->IsVisible()))
    return;

  ToggleSearchKeyboard(hWnd, false);
  if (pBottomMenu == NULL)
  {
    SetMenu(hWnd, pListMenu, false);
  }

  if (pSearchKbd)
    pSearchKbd->Reset();
  if (pSearchBox)
    pSearchBox->Hide();

  pListData->ClearFilter();

  if (redraw)
  {
    if (pBottomMenu != NULL)
      InvalidateRect(hWnd, &pBottomMenu->GetRect(), FALSE);
    pListData->Redraw();

    pListData->ScrollToSelection();
  }
}

void ToggleSearchKeyboard(HWND hWnd, bool bShow)
{
  if (SearchKbdShown != bShow)
  {
    SearchKbdShown = bShow;
    if (bShow)
      pBottomMenu = NULL;
    CalculateRects();

    if (!bShow)
    {
      pListData->SetExtraHeight(0);
      pListData->CalculateHeights();
      //CalculateKeyboard();
    }
  }
}

int GetBackgroundTasks()
{
  int bgTasks = bgtNone;
  if (PoomPictures::IsBusy())
    bgTasks |= bgtPictures;
  if (SimManager::IsBusy())
    bgTasks |= bgtSim;
  return bgTasks;
}

LRESULT DoThreadBusy(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  RECT rc = rTitlebar;
  rc.left = rc.right - 11;
  rc.top = rc.bottom;
  rc.bottom = rc.top + 6;
  ::InvalidateRect(hWnd, &rc, FALSE);
  return 0;
}

LRESULT DoBusyProgress(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
  if ((int)lParam >= 0 && (int)lParam <= 100 &&
    nBusyProgress != (int)lParam)
  {
    nBusyProgress = (int)lParam;

    RECT rc = rTitlebar;
    rc.right = rc.left + (nBusyProgress * (rTitlebar.right - rTitlebar.left)) / 100;
    rc.top = rc.bottom;
    rc.bottom = rc.top + 1;
    HDC hdc = ::GetDC(hWnd);
    ::FillRect(hdc, &rc, Theme::hbrListScrollbarIndicator);
    ::ReleaseDC(hWnd, hdc);
  }
  return 0;
}

bool GetConfigExe(wchar_t *path)
{
  HKEY hk;
  DWORD disp;
  DWORD maxBytes;
  DWORD type;
  bool ret = false;

  if (RegCreateKeyEx(HKEY_LOCAL_MACHINE, L"Software\\Apps\\Avian Waves iContact AE", 0, L"", 0, 0, NULL, &hk, &disp) == ERROR_SUCCESS)
  {
    maxBytes = MAX_PATH * sizeof(wchar_t);

    if (RegQueryValueEx(hk, L"InstallDir", 0, &type, (PBYTE) path, &maxBytes) == ERROR_SUCCESS)
    {
      _tcscat(path, L"\\");
      _tcscat(path, L"\\iContactCfg.exe");
      ret = true;
    }
    
    RegCloseKey(hk);
  }

  return ret;
}

